Coverage for tests/test_cliUtilSplitKv.py : 23%

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/>.
22"""Unit tests for the daf_butler shared CLI options.
23"""
25import click
26from functools import partial
27import unittest
28from unittest.mock import MagicMock
30from lsst.daf.butler.cli.utils import clickResultMsg, LogCliRunner, split_kv
33class SplitKvTestCase(unittest.TestCase):
34 """Tests that call split_kv directly."""
36 def test_single(self):
37 self.assertEqual(split_kv("context", "param", "first=1"), {"first": "1"})
39 def test_multiple(self):
40 self.assertEqual(split_kv("context", "param", "first=1,second=2"), {"first": "1", "second": "2"})
42 def test_unseparated(self):
43 self.assertEqual(split_kv("context", "param", "first,second=2", unseparated_okay=True),
44 {"": "first", "second": "2"})
46 def test_notMultiple(self):
47 with self.assertRaisesRegex(click.ClickException, "Could not parse key-value pair "
48 "'first=1,second=2' using separator '=', with multiple values not "
49 "allowed."):
50 split_kv("context", "param", "first=1,second=2", multiple=False)
52 def test_wrongSeparator(self):
53 with self.assertRaises(click.ClickException):
54 split_kv("context", "param", "first-1")
56 def test_missingSeparator(self):
57 with self.assertRaises(click.ClickException):
58 split_kv("context", "param", "first 1")
60 def test_duplicateKeys(self):
61 with self.assertRaises(click.ClickException):
62 split_kv("context", "param", "first=1,first=2")
64 def test_dashSeparator(self):
65 self.assertEqual(split_kv("context", "param", "first-1,second-2", separator="-"),
66 {"first": "1", "second": "2"})
69class SplitKvCmdTestCase(unittest.TestCase):
70 """Tests using split_kv with a command."""
72 def setUp(self):
73 self.runner = LogCliRunner()
75 def test_cli(self):
76 mock = MagicMock()
78 @click.command()
79 @click.option("--value", callback=split_kv, multiple=True)
80 def cli(value):
81 mock(value)
83 result = self.runner.invoke(cli, ["--value", "first=1"])
84 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
85 mock.assert_called_with({'first': '1'})
87 result = self.runner.invoke(cli, ["--value", "first=1,second=2"])
88 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
89 mock.assert_called_with({'first': '1', 'second': '2'})
91 result = self.runner.invoke(cli, ["--value", "first=1", "--value", "second=2"])
92 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
93 mock.assert_called_with({'first': '1', 'second': '2'})
95 # double separator "==" should fail:
96 result = self.runner.invoke(cli, ["--value", "first==1"])
97 self.assertEqual(result.exit_code, 1)
98 self.assertEqual(result.output,
99 "Error: Could not parse key-value pair 'first==1' using separator '=', with "
100 "multiple values allowed.\n")
102 def test_choice(self):
103 choices = ["FOO", "BAR", "BAZ"]
104 mock = MagicMock()
106 @click.command()
107 @click.option("--metasyntactic-var",
108 callback=partial(split_kv,
109 unseparated_okay=True,
110 choice=click.Choice(choices, case_sensitive=False),
111 normalize=True))
112 def cli(metasyntactic_var):
113 mock(metasyntactic_var)
115 # check a valid choice without a kv separator
116 result = self.runner.invoke(cli, ["--metasyntactic-var", "FOO"])
117 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
118 mock.assert_called_with({"": "FOO"})
120 # check a valid choice with a kv separator
121 result = self.runner.invoke(cli, ["--metasyntactic-var", "lsst.daf.butler=BAR"])
122 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
123 mock.assert_called_with({"lsst.daf.butler": "BAR"})
125 # check invalid choices with and wihtout kv separators
126 for val in ("BOZ", "lsst.daf.butler=BOZ"):
127 result = self.runner.invoke(cli, ["--metasyntactic-var", val])
128 self.assertNotEqual(result.exit_code, 0, msg=clickResultMsg(result))
129 self.assertRegex(result.output,
130 r"Error: Invalid value for ['\"]\-\-metasyntactic-var['\"]:")
131 self.assertIn(f" invalid choice: BOZ. (choose from {', '.join(choices)})",
132 result.output)
134 # check value normalization (lower case "foo" should become "FOO")
135 result = self.runner.invoke(cli, ["--metasyntactic-var", "lsst.daf.butler=foo"])
136 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
137 mock.assert_called_with({"lsst.daf.butler": "FOO"})
139 def test_separatorDash(self):
140 def split_kv_dash(context, param, values):
141 return split_kv(context, param, values, separator="-")
143 mock = MagicMock()
145 @click.command()
146 @click.option("--value", callback=split_kv_dash, multiple=True)
147 def cli(value):
148 mock(value)
150 result = self.runner.invoke(cli, ["--value", "first-1"])
151 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
152 mock.assert_called_with({'first': '1'})
154 def test_separatorFunctoolsDash(self):
155 mock = MagicMock()
157 @click.command()
158 @click.option("--value", callback=partial(split_kv, separator="-"), multiple=True)
159 def cli(value):
160 mock(value)
161 result = self.runner.invoke(cli, ["--value", "first-1", "--value", "second-2"])
162 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
163 mock.assert_called_with({'first': '1', 'second': '2'})
165 def test_separatorSpace(self):
166 @click.command()
167 @click.option("--value", callback=partial(split_kv, separator=" "), multiple=True)
168 def cli(value):
169 pass
170 result = self.runner.invoke(cli, ["--value", "first 1"])
171 self.assertEqual(str(result.exception),
172 "' ' is not a supported separator for key-value pairs.")
174 def test_separatorComma(self):
175 @click.command()
176 @click.option("--value", callback=partial(split_kv, separator=","), multiple=True)
177 def cli(value):
178 pass
179 result = self.runner.invoke(cli, ["--value", "first,1"])
180 self.assertEqual(str(result.exception),
181 "',' is not a supported separator for key-value pairs.")
183 def test_normalizeWithoutChoice(self):
184 """Test that normalize=True without Choice fails gracefully.
186 Normalize uses values in the provided Choice to create the normalized
187 value. Without a provided Choice, it can't normalize. Verify that this
188 does not cause a crash or other bad behavior, it just doesn't normalize
189 anything.
190 """
191 mock = MagicMock()
193 @click.command()
194 @click.option("--value", callback=partial(split_kv, normalize=True))
195 def cli(value):
196 mock(value)
197 result = self.runner.invoke(cli, ["--value", "foo=bar"])
198 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
199 mock.assert_called_with(dict(foo="bar"))
202if __name__ == "__main__": 202 ↛ 203line 202 didn't jump to line 203, because the condition on line 202 was never true
203 unittest.main()