cli: Refactor the cli/command.py module.
In a previous CL, the chromite/cli package was created and files were
copied over from chromite/cros directly without modification (in order
to help gerrit track file movement). This CL cleans up the new package
structure and modifies import paths appropriately throughout chromite to
make the new cli package functional.
The main cleanup here is the removal of code in __init__.py files, which
is confusing and in this case unnecessary. The code is instead put into
a command.py module containing the high-level code to create and find
CLI commands.
BUG=brillo:557
TEST=cbuildbot/run_tests
Change-Id: Ie9dc29585810206eed0e386fa056ceffdf9289a3
Reviewed-on: https://chromium-review.googlesource.com/261181
Reviewed-by: David Pursell <dpursell@chromium.org>
Commit-Queue: David Pursell <dpursell@chromium.org>
Trybot-Ready: David Pursell <dpursell@chromium.org>
Tested-by: David Pursell <dpursell@chromium.org>
diff --git a/cli/command_unittest.py b/cli/command_unittest.py
index 4504bd1..be8b15d 100644
--- a/cli/command_unittest.py
+++ b/cli/command_unittest.py
@@ -2,52 +2,122 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""This module tests the command module."""
+"""Tests for the command module."""
from __future__ import print_function
import argparse
+import glob
+from chromite.cli import command
+from chromite.lib import commandline
+from chromite.lib import cros_build_lib_unittest
+from chromite.lib import cros_import
from chromite.lib import cros_test_lib
-from chromite import cros
+from chromite.lib import partial_mock
+
# pylint:disable=protected-access
_COMMAND_NAME = 'superAwesomeCommandOfFunness'
-@cros.CommandDecorator(_COMMAND_NAME)
-class TestCommand(cros.CrosCommand):
+@command.CommandDecorator(_COMMAND_NAME)
+class TestCommand(command.CrosCommand):
"""A fake command."""
def Run(self):
print('Just testing')
-class CommandTest(cros_test_lib.TestCase):
+class TestCommandTest(cros_test_lib.TestCase):
"""This test class tests that Commands method."""
def testParserSetsCrosClass(self):
"""Tests that our parser sets cros_class correctly."""
my_parser = argparse.ArgumentParser()
- cros.CrosCommand.AddParser(my_parser)
+ command.CrosCommand.AddParser(my_parser)
ns = my_parser.parse_args([])
- self.assertEqual(ns.cros_class, cros.CrosCommand)
+ self.assertEqual(ns.cros_class, command.CrosCommand)
def testCommandDecorator(self):
"""Tests that our decorator correctly adds TestCommand to _commands."""
# Note this exposes an implementation detail of _commands.
- self.assertEqual(cros._commands[_COMMAND_NAME], TestCommand)
+ self.assertEqual(command._commands[_COMMAND_NAME], TestCommand)
def testBadUseOfCommandDecorator(self):
"""Tests that our decorator correctly rejects bad test commands."""
try:
# pylint: disable=W0612
- @cros.CommandDecorator('bad')
+ @command.CommandDecorator('bad')
class BadTestCommand(object):
"""A command that wasn't implemented correctly."""
pass
- except cros.InvalidCommandError:
+ except command.InvalidCommandError:
pass
else:
self.fail('Invalid command was accepted by the CommandDecorator')
+
+
+class MockCommand(partial_mock.PartialMock):
+ """Mock class for a generic cros command."""
+ ATTRS = ('Run',)
+ COMMAND = None
+ TARGET_CLASS = None
+
+ def __init__(self, args, base_args=None):
+ partial_mock.PartialMock.__init__(self)
+ self.args = args
+ self.rc_mock = cros_build_lib_unittest.RunCommandMock()
+ self.rc_mock.SetDefaultCmdResult()
+ parser = commandline.ArgumentParser(caching=True)
+ subparsers = parser.add_subparsers()
+ subparser = subparsers.add_parser(self.COMMAND, caching=True)
+ self.TARGET_CLASS.AddParser(subparser)
+
+ args = base_args if base_args else []
+ args += [self.COMMAND] + self.args
+ options = parser.parse_args(args)
+ self.inst = options.cros_class(options)
+
+ def Run(self, inst):
+ with self.rc_mock:
+ return self.backup['Run'](inst)
+
+
+class CommandTest(cros_test_lib.MockTestCase):
+ """This test class tests that we can load modules correctly."""
+
+ # pylint: disable=W0212
+
+ def testFindModules(self):
+ """Tests that we can return modules correctly when mocking out glob."""
+ fake_command_file = 'cros_command_test.py'
+ filtered_file = 'cros_command_unittest.py'
+ mydir = 'mydir'
+
+ self.PatchObject(glob, 'glob',
+ return_value=[fake_command_file, filtered_file])
+
+ self.assertEqual(command._FindModules(mydir), [fake_command_file])
+
+ def testLoadCommands(self):
+ """Tests import commands correctly."""
+ fake_module = 'cros_command_test'
+ fake_command_file = '%s.py' % fake_module
+ module_path = ('chromite', 'cros', 'commands', fake_module)
+
+ self.PatchObject(command, '_FindModules', return_value=[fake_command_file])
+ # The code doesn't use the return value, so stub it out lazy-like.
+ load_mock = self.PatchObject(cros_import, 'ImportModule', return_value=None)
+
+ command._ImportCommands()
+
+ load_mock.assert_called_with(module_path)
+
+ def testListCommands(self):
+ """Tests we get a sane list back."""
+ cros_commands = command.ListCommands()
+ # Pick some commands that are likely to not go away.
+ self.assertIn('chrome-sdk', cros_commands)
+ self.assertIn('flash', cros_commands)