blob: a7349a0f0311461dfa09b2c66cabfedc676804ff [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
Ralph Nathan74e864d2015-05-11 12:13:53 -070011import os
Chris Sosa90c78502012-10-05 17:07:42 -070012
David Pursellf1c27c12015-03-18 09:51:38 -070013from chromite.cli import command
14from chromite.lib import commandline
15from chromite.lib import cros_build_lib_unittest
16from chromite.lib import cros_import
Ralph Nathan74e864d2015-05-11 12:13:53 -070017from chromite.lib import cros_logging as logging
Chris Sosa90c78502012-10-05 17:07:42 -070018from chromite.lib import cros_test_lib
David Pursellf1c27c12015-03-18 09:51:38 -070019from chromite.lib import partial_mock
20
Chris Sosa90c78502012-10-05 17:07:42 -070021
Don Garrett5bcf1442015-03-13 15:55:02 -070022# pylint:disable=protected-access
23
Chris Sosa90c78502012-10-05 17:07:42 -070024_COMMAND_NAME = 'superAwesomeCommandOfFunness'
25
26
David Pursellf1c27c12015-03-18 09:51:38 -070027@command.CommandDecorator(_COMMAND_NAME)
David Pursellffb90042015-03-23 09:21:41 -070028class TestCommand(command.CliCommand):
Chris Sosa90c78502012-10-05 17:07:42 -070029 """A fake command."""
30 def Run(self):
Mike Frysinger383367e2014-09-16 15:06:17 -040031 print('Just testing')
Chris Sosa90c78502012-10-05 17:07:42 -070032
33
David Pursellf80b8c52015-04-03 12:46:45 -070034class TestCommandTest(cros_test_lib.MockTestCase):
Chris Sosa90c78502012-10-05 17:07:42 -070035 """This test class tests that Commands method."""
36
David Pursellffb90042015-03-23 09:21:41 -070037 def testParserSetsCommandClass(self):
38 """Tests that our parser sets command_class correctly."""
Chris Sosa90c78502012-10-05 17:07:42 -070039 my_parser = argparse.ArgumentParser()
David Pursellffb90042015-03-23 09:21:41 -070040 command.CliCommand.AddParser(my_parser)
Chris Sosa90c78502012-10-05 17:07:42 -070041 ns = my_parser.parse_args([])
David Pursellffb90042015-03-23 09:21:41 -070042 self.assertEqual(ns.command_class, command.CliCommand)
Chris Sosa90c78502012-10-05 17:07:42 -070043
44 def testCommandDecorator(self):
45 """Tests that our decorator correctly adds TestCommand to _commands."""
46 # Note this exposes an implementation detail of _commands.
David Pursellf1c27c12015-03-18 09:51:38 -070047 self.assertEqual(command._commands[_COMMAND_NAME], TestCommand)
Chris Sosa90c78502012-10-05 17:07:42 -070048
49 def testBadUseOfCommandDecorator(self):
50 """Tests that our decorator correctly rejects bad test commands."""
51 try:
Brian Harring984988f2012-10-10 22:53:30 -070052 # pylint: disable=W0612
David Pursellf1c27c12015-03-18 09:51:38 -070053 @command.CommandDecorator('bad')
Chris Sosa90c78502012-10-05 17:07:42 -070054 class BadTestCommand(object):
Brian Harring984988f2012-10-10 22:53:30 -070055 """A command that wasn't implemented correctly."""
Chris Sosa90c78502012-10-05 17:07:42 -070056 pass
57
David Pursellf1c27c12015-03-18 09:51:38 -070058 except command.InvalidCommandError:
Chris Sosa90c78502012-10-05 17:07:42 -070059 pass
60 else:
61 self.fail('Invalid command was accepted by the CommandDecorator')
David Pursellf1c27c12015-03-18 09:51:38 -070062
David Pursellf80b8c52015-04-03 12:46:45 -070063 def testCrosAddDeviceArgument(self):
64 """Tests CliCommand.AddDeviceArgument() for `cros`."""
Ralph Nathan1fc77f22015-04-21 15:05:48 -070065 self.PatchObject(command, 'GetToolset', return_value='cros')
David Pursellf80b8c52015-04-03 12:46:45 -070066 parser = argparse.ArgumentParser()
67 command.CliCommand.AddDeviceArgument(parser)
68 # cros should have a positional device argument.
69 parser.parse_args(['device'])
70 with self.assertRaises(SystemExit):
71 parser.parse_args(['--device', 'device'])
72
73 def testBrilloAddDeviceArgument(self):
74 """Tests CliCommand.AddDeviceArgument() for `brillo`."""
Ralph Nathan1fc77f22015-04-21 15:05:48 -070075 self.PatchObject(command, 'GetToolset', return_value='brillo')
David Pursellf80b8c52015-04-03 12:46:45 -070076 parser = argparse.ArgumentParser()
77 command.CliCommand.AddDeviceArgument(parser)
78 # brillo should have an optional device argument.
79 with self.assertRaises(SystemExit):
80 parser.parse_args(['device'])
81 parser.parse_args(['--device', 'device'])
82
David Pursellf1c27c12015-03-18 09:51:38 -070083
84class MockCommand(partial_mock.PartialMock):
David Pursellffb90042015-03-23 09:21:41 -070085 """Mock class for a generic CLI command."""
David Pursellf1c27c12015-03-18 09:51:38 -070086 ATTRS = ('Run',)
87 COMMAND = None
88 TARGET_CLASS = None
89
90 def __init__(self, args, base_args=None):
91 partial_mock.PartialMock.__init__(self)
92 self.args = args
93 self.rc_mock = cros_build_lib_unittest.RunCommandMock()
94 self.rc_mock.SetDefaultCmdResult()
95 parser = commandline.ArgumentParser(caching=True)
96 subparsers = parser.add_subparsers()
97 subparser = subparsers.add_parser(self.COMMAND, caching=True)
98 self.TARGET_CLASS.AddParser(subparser)
99
100 args = base_args if base_args else []
101 args += [self.COMMAND] + self.args
102 options = parser.parse_args(args)
David Pursellffb90042015-03-23 09:21:41 -0700103 self.inst = options.command_class(options)
David Pursellf1c27c12015-03-18 09:51:38 -0700104
105 def Run(self, inst):
106 with self.rc_mock:
107 return self.backup['Run'](inst)
108
109
110class CommandTest(cros_test_lib.MockTestCase):
111 """This test class tests that we can load modules correctly."""
112
113 # pylint: disable=W0212
114
115 def testFindModules(self):
116 """Tests that we can return modules correctly when mocking out glob."""
117 fake_command_file = 'cros_command_test.py'
118 filtered_file = 'cros_command_unittest.py'
119 mydir = 'mydir'
120
121 self.PatchObject(glob, 'glob',
122 return_value=[fake_command_file, filtered_file])
123
David Pursellffb90042015-03-23 09:21:41 -0700124 self.assertEqual(command._FindModules(mydir, 'cros'), [fake_command_file])
David Pursellf1c27c12015-03-18 09:51:38 -0700125
126 def testLoadCommands(self):
127 """Tests import commands correctly."""
128 fake_module = 'cros_command_test'
129 fake_command_file = '%s.py' % fake_module
David Pursellcfd58872015-03-19 09:15:48 -0700130 module_path = ('chromite', 'cli', 'cros', fake_module)
David Pursellf1c27c12015-03-18 09:51:38 -0700131
132 self.PatchObject(command, '_FindModules', return_value=[fake_command_file])
133 # The code doesn't use the return value, so stub it out lazy-like.
134 load_mock = self.PatchObject(cros_import, 'ImportModule', return_value=None)
135
David Pursellffb90042015-03-23 09:21:41 -0700136 command._ImportCommands('cros')
David Pursellf1c27c12015-03-18 09:51:38 -0700137
138 load_mock.assert_called_with(module_path)
139
David Pursellffb90042015-03-23 09:21:41 -0700140 def testListCrosCommands(self):
141 """Tests we get a sane `cros` list back."""
142 cros_commands = command.ListCommands('cros')
David Pursellf1c27c12015-03-18 09:51:38 -0700143 # Pick some commands that are likely to not go away.
144 self.assertIn('chrome-sdk', cros_commands)
145 self.assertIn('flash', cros_commands)
David Pursellffb90042015-03-23 09:21:41 -0700146
147 def testListBrilloCommands(self):
148 """Tests we get a sane `brillo` list back.
149
150 Needs to be separate from testListCrosCommands() because calling
151 ListCommands() twice with both 'brillo' and 'cros' produces a superset
152 rather than two independent sets.
153 """
154 brillo_commands = command.ListCommands('brillo')
155 # Pick some commands that should be in `cros` but not `brillo`.
156 self.assertNotIn('chrome-sdk', brillo_commands)
157 self.assertNotIn('stage', brillo_commands)
158 # Pick some commands that should be in `brillo`.
159 self.assertIn('debug', brillo_commands)
160 self.assertIn('devices', brillo_commands)
Ralph Nathan74e864d2015-05-11 12:13:53 -0700161
162
163class FileLoggerSetupTest(cros_test_lib.WorkspaceTestCase):
164 """Test that logging to file works correctly."""
165
166 def setUp(self):
167 self.CreateWorkspace()
168
169 def testSetupFileLoggerFilename(self):
170 """Test that the filename and path are correct."""
171 patch_handler = self.PatchObject(logging, 'FileHandler',
172 return_value=logging.StreamHandler())
173 command.SetupFileLogger(filename='foo.log')
174
175 # Test that the filename is correct.
176 patch_handler.assert_called_with(
177 os.path.join(self.workspace_path, 'build/logs', 'foo.log'), mode='w')
178
179 def testSetupFileLoggerNoFilename(self):
180 """Test that the filename and path are correct with no arguments."""
181 patch_handler = self.PatchObject(logging, 'FileHandler',
182 return_value=logging.StreamHandler())
183 command.SetupFileLogger()
184
185 # Test that the filename is correct.
186 patch_handler.assert_called_with(
187 os.path.join(self.workspace_path, 'build/logs', 'brillo.log'), mode='w')
188
189 def testSetupFileLoggerLogLevels(self):
190 """Test that the logger operates at the right level."""
191 command.SetupFileLogger('foo.log', log_level=logging.INFO)
192 logging.getLogger().setLevel(logging.DEBUG)
193 logging.debug('debug')
194 logging.info('info')
195 logging.notice('notice')
196
197 # Test that the logs are correct.
198 logs = open(
199 os.path.join(self.workspace_path, 'build/logs', 'foo.log'), 'r').read()
200 self.assertNotIn('debug', logs)
201 self.assertIn('info', logs)
202 self.assertIn('notice', logs)