Coverage for python/lsst/daf/butler/tests/cliCmdTestBase.py : 34%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22import abc
23import copy
24import os
25from unittest.mock import call
27from ..cli.utils import clickResultMsg, mockEnvVar, LogCliRunner, Mocker
28from ..cli import butler
31class CliCmdTestBase(abc.ABC):
32 """A test case base that is used to verify click command functions import
33 and call their respective script fucntions correctly.
34 """
36 @staticmethod
37 @abc.abstractmethod
38 def defaultExpected():
39 pass
41 @staticmethod
42 @abc.abstractmethod
43 def command():
44 """Get the click.Command being tested."""
45 pass
47 @property
48 def cli(self):
49 """Get the command line interface function under test, can be
50 overridden to test CLIs other than butler."""
51 return butler.cli
53 def setUp(self):
54 Mocker.reset()
55 self.runner = LogCliRunner(env=mockEnvVar)
57 @classmethod
58 def makeExpected(cls, **kwargs):
59 expected = copy.copy(cls.defaultExpected())
60 expected.update(kwargs)
61 return expected
63 def run_command(self, inputs):
64 """Use the LogCliRunner with the mock environment variable set to
65 execute a butler subcommand and parameters specified in inputs.
67 Parameters
68 ----------
69 inputs : [`str`]
70 A list of strings that begins with the subcommand name and is
71 followed by arguments, option keys and option values.
73 Returns
74 -------
75 result : `click.testing.Result`
76 The Result object contains the results from calling
77 self.runner.invoke.
78 """
79 return self.runner.invoke(self.cli, inputs)
81 def run_test(self, inputs, expectedKwargs, withTempFile=None):
82 """Run the subcommand specified in inputs and verify a successful
83 outcome where exit code = 0 and the mock object has been called with
84 the expected arguments.
86 Returns the result object for inspection, e.g. sometimes it's useful to
87 be able to inpsect or print `result.output`.
89 Parameters
90 ----------
91 inputs : [`str`]
92 A list of strings that begins with the subcommand name and is
93 followed by arguments, option keys and option values.
94 expectedKwargs : `dict` [`str`, `str`]
95 The arguments that the subcommand function is expected to have been
96 called with. Keys are the argument name and values are the argument
97 value.
98 withTempFile : `str`, optional
99 If not None, will run in a temporary directory and create a file
100 with the given name, can be used with commands with parameters that
101 require a file to exist.
103 Returns
104 -------
105 result : `click.testing.Result`
106 The result object produced by invocation of the command under test.
107 """
108 with self.runner.isolated_filesystem():
109 if withTempFile is not None:
110 directory, filename = os.path.split(withTempFile)
111 if directory:
112 os.makedirs(os.path.dirname(withTempFile), exist_ok=True)
113 with open(withTempFile, "w") as _:
114 # just need to make the file, don't need to keep it open.
115 pass
116 result = self.run_command(inputs)
117 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
118 if isinstance(expectedKwargs, (list, tuple)):
119 calls = (call(**e) for e in expectedKwargs)
120 else:
121 calls = (call(**expectedKwargs),)
122 Mocker.mock.assert_has_calls(list(calls))
123 return result
125 def run_missing(self, inputs, expectedMsg):
126 """Run the subcommand specified in inputs and verify a failed outcome
127 where exit code != 0 and an expected message has been written to
128 stdout.
130 Parameters
131 ----------
132 inputs : [`str`]
133 A list of strings that begins with the subcommand name and is
134 followed by arguments, option keys and option values.
135 expectedMsg : `str`
136 An error message that should be present in stdout after running the
137 subcommand. Can be a regular expression string.
138 """
139 result = self.run_command(inputs)
140 self.assertNotEqual(result.exit_code, 0, clickResultMsg(result))
141 self.assertRegex(result.stdout, expectedMsg)
143 def test_help(self):
144 self.assertFalse(self.command().get_short_help_str().endswith("..."),
145 msg="The command help message is being truncated to "
146 f"\"{self.command().get_short_help_str()}\". It should be shortened, or define "
147 "@command(short_help=\"something short and helpful\")")