Coverage for tests/test_cliUtilSplitKv.py : 24%

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()
77 @click.command()
78 @click.option("--value", callback=split_kv, multiple=True)
79 def cli(value):
80 mock(value)
82 result = self.runner.invoke(cli, ["--value", "first=1"])
83 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
84 mock.assert_called_with({'first': '1'})
86 result = self.runner.invoke(cli, ["--value", "first=1,second=2"])
87 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
88 mock.assert_called_with({'first': '1', 'second': '2'})
90 result = self.runner.invoke(cli, ["--value", "first=1", "--value", "second=2"])
91 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
92 mock.assert_called_with({'first': '1', 'second': '2'})
94 # double separator "==" should fail:
95 result = self.runner.invoke(cli, ["--value", "first==1"])
96 self.assertEqual(result.exit_code, 1)
97 self.assertEqual(result.output,
98 "Error: Could not parse key-value pair 'first==1' using separator '=', with "
99 "multiple values allowed.\n")
101 def test_choice(self):
102 choices = ["FOO", "BAR", "BAZ"]
103 mock = MagicMock()
104 @click.command()
105 @click.option("--metasyntactic-var",
106 callback=partial(split_kv,
107 unseparated_okay=True,
108 choice=click.Choice(choices, case_sensitive=False),
109 normalize=True))
110 def cli(metasyntactic_var):
111 mock(metasyntactic_var)
113 # check a valid choice without a kv separator
114 result = self.runner.invoke(cli, ["--metasyntactic-var", "FOO"])
115 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
116 mock.assert_called_with({"": "FOO"})
118 # check a valid choice with a kv separator
119 result = self.runner.invoke(cli, ["--metasyntactic-var", "lsst.daf.butler=BAR"])
120 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
121 mock.assert_called_with({"lsst.daf.butler": "BAR"})
123 # check invalid choices with and wihtout kv separators
124 for val in ("BOZ", "lsst.daf.butler=BOZ"):
125 result = self.runner.invoke(cli, ["--metasyntactic-var", val])
126 self.assertNotEqual(result.exit_code, 0, msg=clickResultMsg(result))
127 self.assertIn('Error: Invalid value for "--metasyntactic-var": invalid choice: BOZ. '
128 f'(choose from {", ".join(choices)})',
129 result.output)
131 # check value normalization (lower case "foo" should become "FOO")
132 result = self.runner.invoke(cli, ["--metasyntactic-var", "lsst.daf.butler=foo"])
133 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
134 mock.assert_called_with({"lsst.daf.butler": "FOO"})
136 def test_separatorDash(self):
137 def split_kv_dash(context, param, values):
138 return split_kv(context, param, values, separator="-")
140 mock = MagicMock()
141 @click.command()
142 @click.option("--value", callback=split_kv_dash, multiple=True)
143 def cli(value):
144 mock(value)
146 result = self.runner.invoke(cli, ["--value", "first-1"])
147 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
148 mock.assert_called_with({'first': '1'})
150 def test_separatorFunctoolsDash(self):
151 mock = MagicMock()
152 @click.command()
153 @click.option("--value", callback=partial(split_kv, separator="-"), multiple=True)
154 def cli(value):
155 mock(value)
156 result = self.runner.invoke(cli, ["--value", "first-1", "--value", "second-2"])
157 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
158 mock.assert_called_with({'first': '1', 'second': '2'})
160 def test_separatorSpace(self):
161 @click.command()
162 @click.option("--value", callback=partial(split_kv, separator=" "), multiple=True)
163 def cli(value):
164 pass
165 result = self.runner.invoke(cli, ["--value", "first 1"])
166 self.assertEqual(str(result.exception),
167 "' ' is not a supported separator for key-value pairs.")
169 def test_separatorComma(self):
170 @click.command()
171 @click.option("--value", callback=partial(split_kv, separator=","), multiple=True)
172 def cli(value):
173 pass
174 result = self.runner.invoke(cli, ["--value", "first,1"])
175 self.assertEqual(str(result.exception),
176 "',' is not a supported separator for key-value pairs.")
178 def test_normalizeWithoutChoice(self):
179 """Test that normalize=True without Choice fails gracefully.
181 Normalize uses values in the provided Choice to create the normalized
182 value. Without a provided Choice, it can't normalize. Verify that this
183 does not cause a crash or other bad behavior, it just doesn't normalize
184 anything.
185 """
186 mock = MagicMock()
187 @click.command()
188 @click.option("--value", callback=partial(split_kv, normalize=True))
189 def cli(value):
190 mock(value)
191 result = self.runner.invoke(cli, ["--value", "foo=bar"])
192 self.assertEqual(result.exit_code, 0, msg=clickResultMsg(result))
193 mock.assert_called_with(dict(foo="bar"))
196if __name__ == "__main__": 196 ↛ 197line 196 didn't jump to line 197, because the condition on line 196 was never true
197 unittest.main()