1# --------------------------------------------------------------------------------------------
2# Copyright (c) Microsoft Corporation. All rights reserved.
3# Licensed under the MIT License. See License.txt in the project root for license information.
4# --------------------------------------------------------------------------------------------
5
6import unittest
7try:
8    import mock
9except ImportError:
10    from unittest import mock
11
12import sys
13import argparse
14
15from knack.arguments import ArgumentsContext
16from knack.commands import CLICommandsLoader, CommandGroup
17
18from tests.util import DummyCLI, redirect_io, assert_in_multi_line
19
20
21def example_handler(arg1, arg2=None, arg3=None):
22    """ Short summary here. Long summary here. Still long summary. """
23    pass
24
25
26def example_arg_handler(arg1, opt1, arg2=None, opt2=None, arg3=None,
27                        opt3=None, arg4=None, opt4=None, arg5=None, opt5=None):
28    pass
29
30
31class TestCommandExperimental(unittest.TestCase):
32
33    def setUp(self):
34
35        from knack.help_files import helps
36
37        class ExperimentalTestCommandLoader(CLICommandsLoader):
38            def load_command_table(self, args):
39                super(ExperimentalTestCommandLoader, self).load_command_table(args)
40                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
41                    g.command('cmd1', 'example_handler', is_experimental=True)
42
43                with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), is_experimental=True) as g:
44                    g.command('cmd1', 'example_handler')
45
46                return self.command_table
47
48            def load_arguments(self, command):
49                with ArgumentsContext(self, '') as c:
50                    c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3])
51                    c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c'])
52
53                super(ExperimentalTestCommandLoader, self).load_arguments(command)
54
55        helps['grp1'] = """
56    type: group
57    short-summary: A group.
58"""
59        self.cli_ctx = DummyCLI(commands_loader_cls=ExperimentalTestCommandLoader)
60
61    @redirect_io
62    def test_experimental_command_implicitly_execute(self):
63        """ Ensure general warning displayed when running command from an experimental parent group. """
64        self.cli_ctx.invoke('grp1 cmd1 -b b'.split())
65        actual = self.io.getvalue()
66        expected = "Command group 'grp1' is experimental and under development."
67        self.assertIn(expected, actual)
68
69    @redirect_io
70    def test_experimental_command_group_help(self):
71        """ Ensure experimental commands appear correctly in group help view. """
72        with self.assertRaises(SystemExit):
73            self.cli_ctx.invoke('-h'.split())
74        actual = self.io.getvalue()
75        expected = u"""
76Group
77    {}
78
79Subgroups:
80    grp1 [Experimental] : A group.
81
82Commands:
83    cmd1 [Experimental] : Short summary here.
84
85""".format(self.cli_ctx.name)
86        assert_in_multi_line(expected, actual)
87
88    @redirect_io
89    def test_experimental_command_plain_execute(self):
90        """ Ensure general warning displayed when running experimental command. """
91        self.cli_ctx.invoke('cmd1 -b b'.split())
92        actual = self.io.getvalue()
93        expected = "This command is experimental and under development."
94        self.assertIn(expected, actual)
95
96
97class TestCommandGroupExperimental(unittest.TestCase):
98
99    def setUp(self):
100
101        from knack.help_files import helps
102
103        class ExperimentalTestCommandLoader(CLICommandsLoader):
104            def load_command_table(self, args):
105                super(ExperimentalTestCommandLoader, self).load_command_table(args)
106
107                with CommandGroup(self, 'group1', '{}#{{}}'.format(__name__), is_experimental=True) as g:
108                    g.command('cmd1', 'example_handler')
109
110                return self.command_table
111
112            def load_arguments(self, command):
113                with ArgumentsContext(self, '') as c:
114                    c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3])
115                    c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c'])
116
117                super(ExperimentalTestCommandLoader, self).load_arguments(command)
118
119        helps['group1'] = """
120    type: group
121    short-summary: A group.
122"""
123        self.cli_ctx = DummyCLI(commands_loader_cls=ExperimentalTestCommandLoader)
124
125    @redirect_io
126    def test_experimental_command_group_help_plain(self):
127        """ Ensure help warnings appear for experimental command group help. """
128        with self.assertRaises(SystemExit):
129            self.cli_ctx.invoke('group1 -h'.split())
130        actual = self.io.getvalue()
131        expected = """
132Group
133    cli group1 : A group.
134        This command group is experimental and under development.
135Commands:
136    cmd1 : Short summary here.
137
138""".format(self.cli_ctx.name)
139        assert_in_multi_line(expected, actual)
140
141    @redirect_io
142    def test_experimental_command_implicitly(self):
143        """ Ensure help warning displayed for command in experimental because of a experimental parent group. """
144        with self.assertRaises(SystemExit):
145            self.cli_ctx.invoke('group1 cmd1 -h'.split())
146        actual = self.io.getvalue()
147        expected = """
148Command
149    {} group1 cmd1 : Short summary here.
150        Long summary here. Still long summary.
151        Command group 'group1' is experimental and under development.
152""".format(self.cli_ctx.name)
153        assert_in_multi_line(expected, actual)
154
155
156class TestArgumentExperimental(unittest.TestCase):
157
158    def setUp(self):
159        from knack.help_files import helps
160
161        class LoggerAction(argparse.Action):
162
163            def __call__(self, parser, namespace, values, option_string=None):
164                print("Side-effect from some original action!", file=sys.stderr)
165
166        class ExperimentalTestCommandLoader(CLICommandsLoader):
167            def load_command_table(self, args):
168                super(ExperimentalTestCommandLoader, self).load_command_table(args)
169                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
170                    g.command('arg-test', 'example_arg_handler')
171                return self.command_table
172
173            def load_arguments(self, command):
174                with ArgumentsContext(self, 'arg-test') as c:
175                    c.argument('arg1', help='Arg1', is_experimental=True, action=LoggerAction)
176
177                super(ExperimentalTestCommandLoader, self).load_arguments(command)
178
179        helps['grp1'] = """
180    type: group
181    short-summary: A group.
182"""
183        self.cli_ctx = DummyCLI(commands_loader_cls=ExperimentalTestCommandLoader)
184
185    @redirect_io
186    def test_experimental_arguments_command_help(self):
187        """ Ensure experimental arguments appear correctly in command help view. """
188        with self.assertRaises(SystemExit):
189            self.cli_ctx.invoke('arg-test -h'.split())
190        actual = self.io.getvalue()
191        expected = """
192Arguments
193    --arg1 [Experimental] [Required] : Arg1.
194        Argument '--arg1' is experimental and under development.
195""".format(self.cli_ctx.name)
196        assert_in_multi_line(expected, actual)
197
198    @redirect_io
199    def test_experimental_arguments_execute(self):
200        """ Ensure deprecated arguments can be used. """
201        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
202        actual = self.io.getvalue()
203        experimental_expected = "Argument '--arg1' is experimental and under development."
204        self.assertIn(experimental_expected, actual)
205
206        action_expected = "Side-effect from some original action!"
207        self.assertIn(action_expected, actual)
208
209
210if __name__ == '__main__':
211    unittest.main()
212