blob: cee07ad942884b3780e3b275fa2b19e08df33133 [file] [log] [blame]
Chris Sosa90c78502012-10-05 17:07:42 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
David Pursellf1c27c12015-03-18 09:51:38 -07005"""Tests for the command module."""
Chris Sosa90c78502012-10-05 17:07:42 -07006
Mike Frysinger383367e2014-09-16 15:06:17 -04007from __future__ import print_function
8
Chris Sosa90c78502012-10-05 17:07:42 -07009import argparse
David Pursellf1c27c12015-03-18 09:51:38 -070010import glob
Chris Sosa90c78502012-10-05 17:07:42 -070011
David Pursellf1c27c12015-03-18 09:51:38 -070012from chromite.cli import command
13from chromite.lib import commandline
14from chromite.lib import cros_build_lib_unittest
15from chromite.lib import cros_import
Chris Sosa90c78502012-10-05 17:07:42 -070016from chromite.lib import cros_test_lib
David Pursellf1c27c12015-03-18 09:51:38 -070017from chromite.lib import partial_mock
18
Chris Sosa90c78502012-10-05 17:07:42 -070019
Don Garrett5bcf1442015-03-13 15:55:02 -070020# pylint:disable=protected-access
21
Chris Sosa90c78502012-10-05 17:07:42 -070022_COMMAND_NAME = 'superAwesomeCommandOfFunness'
23
24
David Pursellf1c27c12015-03-18 09:51:38 -070025@command.CommandDecorator(_COMMAND_NAME)
David Pursellffb90042015-03-23 09:21:41 -070026class TestCommand(command.CliCommand):
Chris Sosa90c78502012-10-05 17:07:42 -070027 """A fake command."""
28 def Run(self):
Mike Frysinger383367e2014-09-16 15:06:17 -040029 print('Just testing')
Chris Sosa90c78502012-10-05 17:07:42 -070030
31
David Pursellf80b8c52015-04-03 12:46:45 -070032class TestCommandTest(cros_test_lib.MockTestCase):
Chris Sosa90c78502012-10-05 17:07:42 -070033 """This test class tests that Commands method."""
34
David Pursellffb90042015-03-23 09:21:41 -070035 def testParserSetsCommandClass(self):
36 """Tests that our parser sets command_class correctly."""
Chris Sosa90c78502012-10-05 17:07:42 -070037 my_parser = argparse.ArgumentParser()
David Pursellffb90042015-03-23 09:21:41 -070038 command.CliCommand.AddParser(my_parser)
Chris Sosa90c78502012-10-05 17:07:42 -070039 ns = my_parser.parse_args([])
David Pursellffb90042015-03-23 09:21:41 -070040 self.assertEqual(ns.command_class, command.CliCommand)
Chris Sosa90c78502012-10-05 17:07:42 -070041
42 def testCommandDecorator(self):
43 """Tests that our decorator correctly adds TestCommand to _commands."""
44 # Note this exposes an implementation detail of _commands.
David Pursellf1c27c12015-03-18 09:51:38 -070045 self.assertEqual(command._commands[_COMMAND_NAME], TestCommand)
Chris Sosa90c78502012-10-05 17:07:42 -070046
47 def testBadUseOfCommandDecorator(self):
48 """Tests that our decorator correctly rejects bad test commands."""
49 try:
Brian Harring984988f2012-10-10 22:53:30 -070050 # pylint: disable=W0612
David Pursellf1c27c12015-03-18 09:51:38 -070051 @command.CommandDecorator('bad')
Chris Sosa90c78502012-10-05 17:07:42 -070052 class BadTestCommand(object):
Brian Harring984988f2012-10-10 22:53:30 -070053 """A command that wasn't implemented correctly."""
Chris Sosa90c78502012-10-05 17:07:42 -070054 pass
55
David Pursellf1c27c12015-03-18 09:51:38 -070056 except command.InvalidCommandError:
Chris Sosa90c78502012-10-05 17:07:42 -070057 pass
58 else:
59 self.fail('Invalid command was accepted by the CommandDecorator')
David Pursellf1c27c12015-03-18 09:51:38 -070060
David Pursellf80b8c52015-04-03 12:46:45 -070061 def testCrosAddDeviceArgument(self):
62 """Tests CliCommand.AddDeviceArgument() for `cros`."""
63 self.PatchObject(command, '_GetToolset', return_value='cros')
64 parser = argparse.ArgumentParser()
65 command.CliCommand.AddDeviceArgument(parser)
66 # cros should have a positional device argument.
67 parser.parse_args(['device'])
68 with self.assertRaises(SystemExit):
69 parser.parse_args(['--device', 'device'])
70
71 def testBrilloAddDeviceArgument(self):
72 """Tests CliCommand.AddDeviceArgument() for `brillo`."""
73 self.PatchObject(command, '_GetToolset', return_value='brillo')
74 parser = argparse.ArgumentParser()
75 command.CliCommand.AddDeviceArgument(parser)
76 # brillo should have an optional device argument.
77 with self.assertRaises(SystemExit):
78 parser.parse_args(['device'])
79 parser.parse_args(['--device', 'device'])
80
David Pursellf1c27c12015-03-18 09:51:38 -070081
82class MockCommand(partial_mock.PartialMock):
David Pursellffb90042015-03-23 09:21:41 -070083 """Mock class for a generic CLI command."""
David Pursellf1c27c12015-03-18 09:51:38 -070084 ATTRS = ('Run',)
85 COMMAND = None
86 TARGET_CLASS = None
87
88 def __init__(self, args, base_args=None):
89 partial_mock.PartialMock.__init__(self)
90 self.args = args
91 self.rc_mock = cros_build_lib_unittest.RunCommandMock()
92 self.rc_mock.SetDefaultCmdResult()
93 parser = commandline.ArgumentParser(caching=True)
94 subparsers = parser.add_subparsers()
95 subparser = subparsers.add_parser(self.COMMAND, caching=True)
96 self.TARGET_CLASS.AddParser(subparser)
97
98 args = base_args if base_args else []
99 args += [self.COMMAND] + self.args
100 options = parser.parse_args(args)
David Pursellffb90042015-03-23 09:21:41 -0700101 self.inst = options.command_class(options)
David Pursellf1c27c12015-03-18 09:51:38 -0700102
103 def Run(self, inst):
104 with self.rc_mock:
105 return self.backup['Run'](inst)
106
107
108class CommandTest(cros_test_lib.MockTestCase):
109 """This test class tests that we can load modules correctly."""
110
111 # pylint: disable=W0212
112
113 def testFindModules(self):
114 """Tests that we can return modules correctly when mocking out glob."""
115 fake_command_file = 'cros_command_test.py'
116 filtered_file = 'cros_command_unittest.py'
117 mydir = 'mydir'
118
119 self.PatchObject(glob, 'glob',
120 return_value=[fake_command_file, filtered_file])
121
David Pursellffb90042015-03-23 09:21:41 -0700122 self.assertEqual(command._FindModules(mydir, 'cros'), [fake_command_file])
David Pursellf1c27c12015-03-18 09:51:38 -0700123
124 def testLoadCommands(self):
125 """Tests import commands correctly."""
126 fake_module = 'cros_command_test'
127 fake_command_file = '%s.py' % fake_module
David Pursellcfd58872015-03-19 09:15:48 -0700128 module_path = ('chromite', 'cli', 'cros', fake_module)
David Pursellf1c27c12015-03-18 09:51:38 -0700129
130 self.PatchObject(command, '_FindModules', return_value=[fake_command_file])
131 # The code doesn't use the return value, so stub it out lazy-like.
132 load_mock = self.PatchObject(cros_import, 'ImportModule', return_value=None)
133
David Pursellffb90042015-03-23 09:21:41 -0700134 command._ImportCommands('cros')
David Pursellf1c27c12015-03-18 09:51:38 -0700135
136 load_mock.assert_called_with(module_path)
137
David Pursellffb90042015-03-23 09:21:41 -0700138 def testListCrosCommands(self):
139 """Tests we get a sane `cros` list back."""
140 cros_commands = command.ListCommands('cros')
David Pursellf1c27c12015-03-18 09:51:38 -0700141 # Pick some commands that are likely to not go away.
142 self.assertIn('chrome-sdk', cros_commands)
143 self.assertIn('flash', cros_commands)
David Pursellffb90042015-03-23 09:21:41 -0700144
145 def testListBrilloCommands(self):
146 """Tests we get a sane `brillo` list back.
147
148 Needs to be separate from testListCrosCommands() because calling
149 ListCommands() twice with both 'brillo' and 'cros' produces a superset
150 rather than two independent sets.
151 """
152 brillo_commands = command.ListCommands('brillo')
153 # Pick some commands that should be in `cros` but not `brillo`.
154 self.assertNotIn('chrome-sdk', brillo_commands)
155 self.assertNotIn('stage', brillo_commands)
156 # Pick some commands that should be in `brillo`.
157 self.assertIn('debug', brillo_commands)
158 self.assertIn('devices', brillo_commands)