Hide keyboard shortcuts

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/>. 

21 

22"""Unit tests for the daf_butler shared CLI options. 

23""" 

24 

25import click 

26from functools import partial 

27import unittest 

28from unittest.mock import MagicMock 

29 

30from lsst.daf.butler.cli.utils import clickResultMsg, LogCliRunner, split_kv 

31 

32 

33class SplitKvTestCase(unittest.TestCase): 

34 """Tests that call split_kv directly.""" 

35 

36 def test_single(self): 

37 self.assertEqual(split_kv("context", "param", "first=1"), {"first": "1"}) 

38 

39 def test_multiple(self): 

40 self.assertEqual(split_kv("context", "param", "first=1,second=2"), {"first": "1", "second": "2"}) 

41 

42 def test_unseparated(self): 

43 self.assertEqual(split_kv("context", "param", "first,second=2", unseparated_okay=True), 

44 {"": "first", "second": "2"}) 

45 

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) 

51 

52 def test_wrongSeparator(self): 

53 with self.assertRaises(click.ClickException): 

54 split_kv("context", "param", "first-1") 

55 

56 def test_missingSeparator(self): 

57 with self.assertRaises(click.ClickException): 

58 split_kv("context", "param", "first 1") 

59 

60 def test_duplicateKeys(self): 

61 with self.assertRaises(click.ClickException): 

62 split_kv("context", "param", "first=1,first=2") 

63 

64 def test_dashSeparator(self): 

65 self.assertEqual(split_kv("context", "param", "first-1,second-2", separator="-"), 

66 {"first": "1", "second": "2"}) 

67 

68 

69class SplitKvCmdTestCase(unittest.TestCase): 

70 """Tests using split_kv with a command.""" 

71 

72 def setUp(self): 

73 self.runner = LogCliRunner() 

74 

75 def test_cli(self): 

76 mock = MagicMock() 

77 

78 @click.command() 

79 @click.option("--value", callback=split_kv, multiple=True) 

80 def cli(value): 

81 mock(value) 

82 

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'}) 

86 

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'}) 

90 

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'}) 

94 

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") 

101 

102 def test_choice(self): 

103 choices = ["FOO", "BAR", "BAZ"] 

104 mock = MagicMock() 

105 

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) 

114 

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"}) 

119 

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"}) 

124 

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) 

133 

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"}) 

138 

139 def test_separatorDash(self): 

140 def split_kv_dash(context, param, values): 

141 return split_kv(context, param, values, separator="-") 

142 

143 mock = MagicMock() 

144 

145 @click.command() 

146 @click.option("--value", callback=split_kv_dash, multiple=True) 

147 def cli(value): 

148 mock(value) 

149 

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'}) 

153 

154 def test_separatorFunctoolsDash(self): 

155 mock = MagicMock() 

156 

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'}) 

164 

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.") 

173 

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.") 

182 

183 def test_normalizeWithoutChoice(self): 

184 """Test that normalize=True without Choice fails gracefully. 

185 

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() 

192 

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")) 

200 

201 

202if __name__ == "__main__": 202 ↛ 203line 202 didn't jump to line 203, because the condition on line 202 was never true

203 unittest.main()