4 # Copyright (C) 2011 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for testing ganeti.utils.text"""
30 from cStringIO import StringIO
32 from ganeti import constants
33 from ganeti import utils
34 from ganeti import errors
39 class TestMatchNameComponent(unittest.TestCase):
40 """Test case for the MatchNameComponent function"""
42 def testEmptyList(self):
43 """Test that there is no match against an empty list"""
44 self.failUnlessEqual(utils.MatchNameComponent("", []), None)
45 self.failUnlessEqual(utils.MatchNameComponent("test", []), None)
47 def testSingleMatch(self):
48 """Test that a single match is performed correctly"""
49 mlist = ["test1.example.com", "test2.example.com", "test3.example.com"]
50 for key in "test2", "test2.example", "test2.example.com":
51 self.failUnlessEqual(utils.MatchNameComponent(key, mlist), mlist[1])
53 def testMultipleMatches(self):
54 """Test that a multiple match is returned as None"""
55 mlist = ["test1.example.com", "test1.example.org", "test1.example.net"]
56 for key in "test1", "test1.example":
57 self.failUnlessEqual(utils.MatchNameComponent(key, mlist), None)
59 def testFullMatch(self):
60 """Test that a full match is returned correctly"""
62 key2 = "test1.example"
63 mlist = [key2, key2 + ".com"]
64 self.failUnlessEqual(utils.MatchNameComponent(key1, mlist), None)
65 self.failUnlessEqual(utils.MatchNameComponent(key2, mlist), key2)
67 def testCaseInsensitivePartialMatch(self):
68 """Test for the case_insensitive keyword"""
69 mlist = ["test1.example.com", "test2.example.net"]
70 self.assertEqual(utils.MatchNameComponent("test2", mlist,
71 case_sensitive=False),
73 self.assertEqual(utils.MatchNameComponent("Test2", mlist,
74 case_sensitive=False),
76 self.assertEqual(utils.MatchNameComponent("teSt2", mlist,
77 case_sensitive=False),
79 self.assertEqual(utils.MatchNameComponent("TeSt2", mlist,
80 case_sensitive=False),
83 def testCaseInsensitiveFullMatch(self):
84 mlist = ["ts1.ex", "ts1.ex.org", "ts2.ex", "Ts2.ex"]
86 # Between the two ts1 a full string match non-case insensitive should work
87 self.assertEqual(utils.MatchNameComponent("Ts1", mlist,
88 case_sensitive=False),
90 self.assertEqual(utils.MatchNameComponent("Ts1.ex", mlist,
91 case_sensitive=False),
93 self.assertEqual(utils.MatchNameComponent("ts1.ex", mlist,
94 case_sensitive=False),
97 # Between the two ts2 only case differs, so only case-match works
98 self.assertEqual(utils.MatchNameComponent("ts2.ex", mlist,
99 case_sensitive=False),
101 self.assertEqual(utils.MatchNameComponent("Ts2.ex", mlist,
102 case_sensitive=False),
104 self.assertEqual(utils.MatchNameComponent("TS2.ex", mlist,
105 case_sensitive=False),
109 class TestDnsNameGlobPattern(unittest.TestCase):
113 "node2-0.example.com",
114 "node2-1.example.com",
118 "sub.site.example.com",
121 def _Test(self, pattern):
122 re_pat = utils.DnsNameGlobPattern(pattern)
124 return filter(re.compile(re_pat).match, self.names)
127 for pattern in ["xyz", "node", " ", "example.net", "x*.example.*",
129 self.assertEqual(self._Test(pattern), [])
131 for pattern in ["*", "???*"]:
132 self.assertEqual(self._Test(pattern), self.names)
134 self.assertEqual(self._Test("node1.*.net"), ["node1.example.net"])
135 self.assertEqual(self._Test("*.example.net"), ["node1.example.net"])
136 self.assertEqual(self._Test("web1.example.com"), ["web1.example.com"])
138 for pattern in ["*.*.*.*", "???", "*.site"]:
139 self.assertEqual(self._Test(pattern), ["sub.site.example.com"])
141 self.assertEqual(self._Test("node1"), [
145 self.assertEqual(self._Test("node?*.example.*"), [
147 "node2-0.example.com",
148 "node2-1.example.com",
151 self.assertEqual(self._Test("*-?"), [
152 "node2-0.example.com",
153 "node2-1.example.com",
155 self.assertEqual(self._Test("node2-?.example.com"), [
156 "node2-0.example.com",
157 "node2-1.example.com",
161 class TestFormatUnit(unittest.TestCase):
162 """Test case for the FormatUnit function"""
165 self.assertEqual(utils.FormatUnit(1, "h"), "1M")
166 self.assertEqual(utils.FormatUnit(100, "h"), "100M")
167 self.assertEqual(utils.FormatUnit(1023, "h"), "1023M")
169 self.assertEqual(utils.FormatUnit(1, "m"), "1")
170 self.assertEqual(utils.FormatUnit(100, "m"), "100")
171 self.assertEqual(utils.FormatUnit(1023, "m"), "1023")
173 self.assertEqual(utils.FormatUnit(1024, "m"), "1024")
174 self.assertEqual(utils.FormatUnit(1536, "m"), "1536")
175 self.assertEqual(utils.FormatUnit(17133, "m"), "17133")
176 self.assertEqual(utils.FormatUnit(1024 * 1024 - 1, "m"), "1048575")
179 self.assertEqual(utils.FormatUnit(1024, "h"), "1.0G")
180 self.assertEqual(utils.FormatUnit(1536, "h"), "1.5G")
181 self.assertEqual(utils.FormatUnit(17133, "h"), "16.7G")
182 self.assertEqual(utils.FormatUnit(1024 * 1024 - 1, "h"), "1024.0G")
184 self.assertEqual(utils.FormatUnit(1024, "g"), "1.0")
185 self.assertEqual(utils.FormatUnit(1536, "g"), "1.5")
186 self.assertEqual(utils.FormatUnit(17133, "g"), "16.7")
187 self.assertEqual(utils.FormatUnit(1024 * 1024 - 1, "g"), "1024.0")
189 self.assertEqual(utils.FormatUnit(1024 * 1024, "g"), "1024.0")
190 self.assertEqual(utils.FormatUnit(5120 * 1024, "g"), "5120.0")
191 self.assertEqual(utils.FormatUnit(29829 * 1024, "g"), "29829.0")
194 self.assertEqual(utils.FormatUnit(1024 * 1024, "h"), "1.0T")
195 self.assertEqual(utils.FormatUnit(5120 * 1024, "h"), "5.0T")
196 self.assertEqual(utils.FormatUnit(29829 * 1024, "h"), "29.1T")
198 self.assertEqual(utils.FormatUnit(1024 * 1024, "t"), "1.0")
199 self.assertEqual(utils.FormatUnit(5120 * 1024, "t"), "5.0")
200 self.assertEqual(utils.FormatUnit(29829 * 1024, "t"), "29.1")
202 def testErrors(self):
203 self.assertRaises(errors.ProgrammerError, utils.FormatUnit, 1, "a")
206 class TestParseUnit(unittest.TestCase):
207 """Test case for the ParseUnit function"""
210 ("M", 1), ("G", 1024), ("T", 1024 * 1024),
211 ("MB", 1), ("GB", 1024), ("TB", 1024 * 1024),
212 ("MiB", 1), ("GiB", 1024), ("TiB", 1024 * 1024))
214 def testRounding(self):
215 self.assertEqual(utils.ParseUnit("0"), 0)
216 self.assertEqual(utils.ParseUnit("1"), 4)
217 self.assertEqual(utils.ParseUnit("2"), 4)
218 self.assertEqual(utils.ParseUnit("3"), 4)
220 self.assertEqual(utils.ParseUnit("124"), 124)
221 self.assertEqual(utils.ParseUnit("125"), 128)
222 self.assertEqual(utils.ParseUnit("126"), 128)
223 self.assertEqual(utils.ParseUnit("127"), 128)
224 self.assertEqual(utils.ParseUnit("128"), 128)
225 self.assertEqual(utils.ParseUnit("129"), 132)
226 self.assertEqual(utils.ParseUnit("130"), 132)
228 def testFloating(self):
229 self.assertEqual(utils.ParseUnit("0"), 0)
230 self.assertEqual(utils.ParseUnit("0.5"), 4)
231 self.assertEqual(utils.ParseUnit("1.75"), 4)
232 self.assertEqual(utils.ParseUnit("1.99"), 4)
233 self.assertEqual(utils.ParseUnit("2.00"), 4)
234 self.assertEqual(utils.ParseUnit("2.01"), 4)
235 self.assertEqual(utils.ParseUnit("3.99"), 4)
236 self.assertEqual(utils.ParseUnit("4.00"), 4)
237 self.assertEqual(utils.ParseUnit("4.01"), 8)
238 self.assertEqual(utils.ParseUnit("1.5G"), 1536)
239 self.assertEqual(utils.ParseUnit("1.8G"), 1844)
240 self.assertEqual(utils.ParseUnit("8.28T"), 8682212)
242 def testSuffixes(self):
243 for sep in ("", " ", " ", "\t", "\t "):
244 for suffix, scale in self.SCALES:
245 for func in (lambda x: x, str.lower, str.upper):
246 self.assertEqual(utils.ParseUnit("1024" + sep + func(suffix)),
249 def testInvalidInput(self):
250 for sep in ("-", "_", ",", "a"):
251 for suffix, _ in self.SCALES:
252 self.assertRaises(errors.UnitParseError, utils.ParseUnit,
255 for suffix, _ in self.SCALES:
256 self.assertRaises(errors.UnitParseError, utils.ParseUnit,
260 class TestShellQuoting(unittest.TestCase):
261 """Test case for shell quoting functions"""
263 def testShellQuote(self):
264 self.assertEqual(utils.ShellQuote('abc'), "abc")
265 self.assertEqual(utils.ShellQuote('ab"c'), "'ab\"c'")
266 self.assertEqual(utils.ShellQuote("a'bc"), "'a'\\''bc'")
267 self.assertEqual(utils.ShellQuote("a b c"), "'a b c'")
268 self.assertEqual(utils.ShellQuote("a b\\ c"), "'a b\\ c'")
270 def testShellQuoteArgs(self):
271 self.assertEqual(utils.ShellQuoteArgs(['a', 'b', 'c']), "a b c")
272 self.assertEqual(utils.ShellQuoteArgs(['a', 'b"', 'c']), "a 'b\"' c")
273 self.assertEqual(utils.ShellQuoteArgs(['a', 'b\'', 'c']), "a 'b'\\\''' c")
276 class TestShellWriter(unittest.TestCase):
279 sw = utils.ShellWriter(buf)
280 sw.Write("#!/bin/bash")
281 sw.Write("if true; then")
284 sw.Write("echo true")
286 sw.Write("for i in 1 2 3")
290 self.assertEqual(sw._indent, 2)
297 sw.Write("echo %s", utils.ShellQuote("Hello World"))
300 self.assertEqual(sw._indent, 0)
302 output = buf.getvalue()
304 self.assert_(output.endswith("\n"))
306 lines = output.splitlines()
307 self.assertEqual(len(lines), 9)
308 self.assertEqual(lines[0], "#!/bin/bash")
309 self.assert_(re.match(r"^\s+date$", lines[5]))
310 self.assertEqual(lines[7], "echo 'Hello World'")
314 sw = utils.ShellWriter(buf)
316 self.assertEqual(buf.getvalue(), "")
318 def testEmptyLines(self):
320 sw = utils.ShellWriter(buf)
322 def _AddLevel(level):
327 # Add empty line, it should not be indented
336 self.assertEqual(buf.getvalue(),
337 "".join("\n%s%s\n" % (i * " ", i) for i in range(1, 6)))
340 class TestNormalizeAndValidateMac(unittest.TestCase):
341 def testInvalid(self):
342 self.assertRaises(errors.OpPrereqError,
343 utils.NormalizeAndValidateMac, "xxx")
345 def testNormalization(self):
346 for mac in ["aa:bb:cc:dd:ee:ff", "00:AA:11:bB:22:cc"]:
347 self.assertEqual(utils.NormalizeAndValidateMac(mac), mac.lower())
350 class TestSafeEncode(unittest.TestCase):
351 """Test case for SafeEncode"""
354 for txt in [string.digits, string.letters, string.punctuation]:
355 self.failUnlessEqual(txt, utils.SafeEncode(txt))
357 def testDoubleEncode(self):
359 txt = utils.SafeEncode(chr(i))
360 self.failUnlessEqual(txt, utils.SafeEncode(txt))
362 def testUnicode(self):
363 # 1024 is high enough to catch non-direct ASCII mappings
364 for i in range(1024):
365 txt = utils.SafeEncode(unichr(i))
366 self.failUnlessEqual(txt, utils.SafeEncode(txt))
369 class TestUnescapeAndSplit(unittest.TestCase):
370 """Testing case for UnescapeAndSplit"""
373 # testing more that one separator for regexp safety
374 self._seps = [",", "+", ".", ":"]
376 def testSimple(self):
377 a = ["a", "b", "c", "d"]
378 for sep in self._seps:
379 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), a)
381 def testEscape(self):
382 for sep in self._seps:
383 a = ["a", "b\\" + sep + "c", "d"]
384 b = ["a", "b" + sep + "c", "d"]
385 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
387 def testDoubleEscape(self):
388 for sep in self._seps:
389 a = ["a", "b\\\\", "c", "d"]
390 b = ["a", "b\\", "c", "d"]
391 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
393 def testThreeEscape(self):
394 for sep in self._seps:
395 a = ["a", "b\\\\\\" + sep + "c", "d"]
396 b = ["a", "b\\" + sep + "c", "d"]
397 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
399 def testEscapeAtEnd(self):
400 for sep in self._seps:
401 self.assertEqual(utils.UnescapeAndSplit("\\", sep=sep), ["\\"])
403 a = ["a", "b\\", "c"]
404 b = ["a", "b" + sep + "c\\"]
405 self.assertEqual(utils.UnescapeAndSplit("%s\\" % sep.join(a), sep=sep), b)
407 a = ["\\" + sep, "\\" + sep, "c", "d\\.moo"]
408 b = [sep, sep, "c", "d.moo\\"]
409 self.assertEqual(utils.UnescapeAndSplit("%s\\" % sep.join(a), sep=sep), b)
411 def testMultipleEscapes(self):
412 for sep in self._seps:
413 a = ["a", "b\\" + sep + "c", "d\\" + sep + "e\\" + sep + "f", "g"]
414 b = ["a", "b" + sep + "c", "d" + sep + "e" + sep + "f", "g"]
415 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
418 class TestCommaJoin(unittest.TestCase):
420 self.assertEqual(utils.CommaJoin([]), "")
421 self.assertEqual(utils.CommaJoin([1, 2, 3]), "1, 2, 3")
422 self.assertEqual(utils.CommaJoin(["Hello"]), "Hello")
423 self.assertEqual(utils.CommaJoin(["Hello", "World"]), "Hello, World")
424 self.assertEqual(utils.CommaJoin(["Hello", "World", 99]),
428 class TestFormatTime(unittest.TestCase):
429 """Testing case for FormatTime"""
432 def _TestInProcess(tz, timestamp, usecs, expected):
433 os.environ["TZ"] = tz
435 return utils.FormatTime(timestamp, usecs=usecs) == expected
437 def _Test(self, *args):
438 # Need to use separate process as we want to change TZ
439 self.assert_(utils.RunInSeparateProcess(self._TestInProcess, *args))
442 self._Test("UTC", 0, None, "1970-01-01 00:00:00")
443 self._Test("America/Sao_Paulo", 1292606926, None, "2010-12-17 15:28:46")
444 self._Test("Europe/London", 1292606926, None, "2010-12-17 17:28:46")
445 self._Test("Europe/Zurich", 1292606926, None, "2010-12-17 18:28:46")
446 self._Test("Europe/Zurich", 1332944288, 8787, "2012-03-28 16:18:08.008787")
447 self._Test("Australia/Sydney", 1292606926, None, "2010-12-18 04:28:46")
448 self._Test("Australia/Sydney", 1292606926, None, "2010-12-18 04:28:46")
449 self._Test("Australia/Sydney", 1292606926, 999999,
450 "2010-12-18 04:28:46.999999")
453 self.failUnlessEqual(utils.FormatTime(None), "N/A")
455 def testInvalid(self):
456 self.failUnlessEqual(utils.FormatTime(()), "N/A")
459 # tests that we accept time.time input
460 utils.FormatTime(time.time())
461 # tests that we accept int input
462 utils.FormatTime(int(time.time()))
465 class TestFormatSeconds(unittest.TestCase):
467 self.assertEqual(utils.FormatSeconds(1), "1s")
468 self.assertEqual(utils.FormatSeconds(3600), "1h 0m 0s")
469 self.assertEqual(utils.FormatSeconds(3599), "59m 59s")
470 self.assertEqual(utils.FormatSeconds(7200), "2h 0m 0s")
471 self.assertEqual(utils.FormatSeconds(7201), "2h 0m 1s")
472 self.assertEqual(utils.FormatSeconds(7281), "2h 1m 21s")
473 self.assertEqual(utils.FormatSeconds(29119), "8h 5m 19s")
474 self.assertEqual(utils.FormatSeconds(19431228), "224d 21h 33m 48s")
475 self.assertEqual(utils.FormatSeconds(-1), "-1s")
476 self.assertEqual(utils.FormatSeconds(-282), "-282s")
477 self.assertEqual(utils.FormatSeconds(-29119), "-29119s")
480 self.assertEqual(utils.FormatSeconds(1.3), "1s")
481 self.assertEqual(utils.FormatSeconds(1.9), "2s")
482 self.assertEqual(utils.FormatSeconds(3912.12311), "1h 5m 12s")
483 self.assertEqual(utils.FormatSeconds(3912.8), "1h 5m 13s")
486 class TestLineSplitter(unittest.TestCase):
489 ls = utils.LineSplitter(lines.append)
490 ls.write("Hello World\n")
491 self.assertEqual(lines, [])
492 ls.write("Foo\n Bar\r\n ")
495 self.assertEqual(lines, [])
497 self.assertEqual(lines, ["Hello World", "Foo", " Bar"])
499 self.assertEqual(lines, ["Hello World", "Foo", " Bar", " BazMoo"])
501 def _testExtra(self, line, all_lines, p1, p2):
502 self.assertEqual(p1, 999)
503 self.assertEqual(p2, "extra")
504 all_lines.append(line)
506 def testExtraArgsNoFlush(self):
508 ls = utils.LineSplitter(self._testExtra, lines, 999, "extra")
509 ls.write("\n\nHello World\n")
510 ls.write("Foo\n Bar\r\n ")
513 ls.write("Moo\n\nx\n")
514 self.assertEqual(lines, [])
516 self.assertEqual(lines, ["", "", "Hello World", "Foo", " Bar", " BazMoo",
520 class TestIsValidShellParam(unittest.TestCase):
526 self.assertEqual(utils.IsValidShellParam(val), result)
529 class TestBuildShellCmd(unittest.TestCase):
531 self.assertRaises(errors.ProgrammerError, utils.BuildShellCmd,
533 self.assertEqual(utils.BuildShellCmd("ls %s", "ab"), "ls ab")
536 class TestOrdinal(unittest.TestCase):
539 0: "0th", 1: "1st", 2: "2nd", 3: "3rd", 4: "4th", 5: "5th", 6: "6th",
540 7: "7th", 8: "8th", 9: "9th", 10: "10th", 11: "11th", 12: "12th",
541 13: "13th", 14: "14th", 15: "15th", 16: "16th", 17: "17th",
542 18: "18th", 19: "19th", 20: "20th", 21: "21st", 25: "25th", 30: "30th",
543 32: "32nd", 40: "40th", 50: "50th", 55: "55th", 60: "60th", 62: "62nd",
544 70: "70th", 80: "80th", 83: "83rd", 90: "90th", 91: "91st",
545 582: "582nd", 999: "999th",
548 for value, ordinal in checks.items():
549 self.assertEqual(utils.FormatOrdinal(value), ordinal)
552 class TestTruncate(unittest.TestCase):
553 def _Test(self, text, length):
554 result = utils.Truncate(text, length)
555 self.assertTrue(len(result) <= length)
559 self.assertEqual(self._Test("", 80), "")
560 self.assertEqual(self._Test("abc", 4), "abc")
561 self.assertEqual(self._Test("Hello World", 80), "Hello World")
562 self.assertEqual(self._Test("Hello World", 4), "H...")
563 self.assertEqual(self._Test("Hello World", 5), "He...")
565 for i in [4, 10, 100]:
566 data = i * "FooBarBaz"
567 self.assertEqual(self._Test(data, len(data)), data)
569 for (length, exp) in [(8, u"T\u00e4st\u2026xyz"), (7, u"T\u00e4st...")]:
570 self.assertEqual(self._Test(u"T\u00e4st\u2026xyz", length), exp)
572 self.assertEqual(self._Test(range(100), 20), "[0, 1, 2, 3, 4, 5...")
576 self.assertRaises(AssertionError, utils.Truncate, "", i)
579 if __name__ == "__main__":
580 testutils.GanetiTestProgram()