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 testEmptyNoIndent(self):
320 sw = utils.ShellWriter(buf, indent=False)
322 self.assertEqual(buf.getvalue(), "")
325 def _AddLevel(cls, sw, level):
331 # Add empty line, it should not be indented
334 cls._AddLevel(sw, level + 1)
338 def testEmptyLines(self):
340 sw = utils.ShellWriter(buf)
342 self._AddLevel(sw, 1)
344 self.assertEqual(buf.getvalue(),
345 "".join("\n%s%s\n" % (i * " ", i) for i in range(1, 6)))
347 def testEmptyLinesNoIndent(self):
349 sw = utils.ShellWriter(buf, indent=False)
351 self._AddLevel(sw, 1)
353 self.assertEqual(buf.getvalue(),
354 "".join("\n%s\n" % i for i in range(1, 6)))
357 class TestNormalizeAndValidateMac(unittest.TestCase):
358 def testInvalid(self):
359 self.assertRaises(errors.OpPrereqError,
360 utils.NormalizeAndValidateMac, "xxx")
362 def testNormalization(self):
363 for mac in ["aa:bb:cc:dd:ee:ff", "00:AA:11:bB:22:cc"]:
364 self.assertEqual(utils.NormalizeAndValidateMac(mac), mac.lower())
367 class TestSafeEncode(unittest.TestCase):
368 """Test case for SafeEncode"""
371 for txt in [string.digits, string.letters, string.punctuation]:
372 self.failUnlessEqual(txt, utils.SafeEncode(txt))
374 def testDoubleEncode(self):
376 txt = utils.SafeEncode(chr(i))
377 self.failUnlessEqual(txt, utils.SafeEncode(txt))
379 def testUnicode(self):
380 # 1024 is high enough to catch non-direct ASCII mappings
381 for i in range(1024):
382 txt = utils.SafeEncode(unichr(i))
383 self.failUnlessEqual(txt, utils.SafeEncode(txt))
386 class TestUnescapeAndSplit(unittest.TestCase):
387 """Testing case for UnescapeAndSplit"""
390 # testing more that one separator for regexp safety
391 self._seps = [",", "+", ".", ":"]
393 def testSimple(self):
394 a = ["a", "b", "c", "d"]
395 for sep in self._seps:
396 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), a)
398 def testEscape(self):
399 for sep in self._seps:
400 a = ["a", "b\\" + sep + "c", "d"]
401 b = ["a", "b" + sep + "c", "d"]
402 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
404 def testDoubleEscape(self):
405 for sep in self._seps:
406 a = ["a", "b\\\\", "c", "d"]
407 b = ["a", "b\\", "c", "d"]
408 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
410 def testThreeEscape(self):
411 for sep in self._seps:
412 a = ["a", "b\\\\\\" + sep + "c", "d"]
413 b = ["a", "b\\" + sep + "c", "d"]
414 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
416 def testEscapeAtEnd(self):
417 for sep in self._seps:
418 self.assertEqual(utils.UnescapeAndSplit("\\", sep=sep), ["\\"])
420 a = ["a", "b\\", "c"]
421 b = ["a", "b" + sep + "c\\"]
422 self.assertEqual(utils.UnescapeAndSplit("%s\\" % sep.join(a), sep=sep), b)
424 a = ["\\" + sep, "\\" + sep, "c", "d\\.moo"]
425 b = [sep, sep, "c", "d.moo\\"]
426 self.assertEqual(utils.UnescapeAndSplit("%s\\" % sep.join(a), sep=sep), b)
428 def testMultipleEscapes(self):
429 for sep in self._seps:
430 a = ["a", "b\\" + sep + "c", "d\\" + sep + "e\\" + sep + "f", "g"]
431 b = ["a", "b" + sep + "c", "d" + sep + "e" + sep + "f", "g"]
432 self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b)
435 class TestCommaJoin(unittest.TestCase):
437 self.assertEqual(utils.CommaJoin([]), "")
438 self.assertEqual(utils.CommaJoin([1, 2, 3]), "1, 2, 3")
439 self.assertEqual(utils.CommaJoin(["Hello"]), "Hello")
440 self.assertEqual(utils.CommaJoin(["Hello", "World"]), "Hello, World")
441 self.assertEqual(utils.CommaJoin(["Hello", "World", 99]),
445 class TestFormatTime(unittest.TestCase):
446 """Testing case for FormatTime"""
449 def _TestInProcess(tz, timestamp, usecs, expected):
450 os.environ["TZ"] = tz
452 return utils.FormatTime(timestamp, usecs=usecs) == expected
454 def _Test(self, *args):
455 # Need to use separate process as we want to change TZ
456 self.assert_(utils.RunInSeparateProcess(self._TestInProcess, *args))
459 self._Test("UTC", 0, None, "1970-01-01 00:00:00")
460 self._Test("America/Sao_Paulo", 1292606926, None, "2010-12-17 15:28:46")
461 self._Test("Europe/London", 1292606926, None, "2010-12-17 17:28:46")
462 self._Test("Europe/Zurich", 1292606926, None, "2010-12-17 18:28:46")
463 self._Test("Europe/Zurich", 1332944288, 8787, "2012-03-28 16:18:08.008787")
464 self._Test("Australia/Sydney", 1292606926, None, "2010-12-18 04:28:46")
465 self._Test("Australia/Sydney", 1292606926, None, "2010-12-18 04:28:46")
466 self._Test("Australia/Sydney", 1292606926, 999999,
467 "2010-12-18 04:28:46.999999")
470 self.failUnlessEqual(utils.FormatTime(None), "N/A")
472 def testInvalid(self):
473 self.failUnlessEqual(utils.FormatTime(()), "N/A")
476 # tests that we accept time.time input
477 utils.FormatTime(time.time())
478 # tests that we accept int input
479 utils.FormatTime(int(time.time()))
482 class TestFormatSeconds(unittest.TestCase):
484 self.assertEqual(utils.FormatSeconds(1), "1s")
485 self.assertEqual(utils.FormatSeconds(3600), "1h 0m 0s")
486 self.assertEqual(utils.FormatSeconds(3599), "59m 59s")
487 self.assertEqual(utils.FormatSeconds(7200), "2h 0m 0s")
488 self.assertEqual(utils.FormatSeconds(7201), "2h 0m 1s")
489 self.assertEqual(utils.FormatSeconds(7281), "2h 1m 21s")
490 self.assertEqual(utils.FormatSeconds(29119), "8h 5m 19s")
491 self.assertEqual(utils.FormatSeconds(19431228), "224d 21h 33m 48s")
492 self.assertEqual(utils.FormatSeconds(-1), "-1s")
493 self.assertEqual(utils.FormatSeconds(-282), "-282s")
494 self.assertEqual(utils.FormatSeconds(-29119), "-29119s")
497 self.assertEqual(utils.FormatSeconds(1.3), "1s")
498 self.assertEqual(utils.FormatSeconds(1.9), "2s")
499 self.assertEqual(utils.FormatSeconds(3912.12311), "1h 5m 12s")
500 self.assertEqual(utils.FormatSeconds(3912.8), "1h 5m 13s")
503 class TestLineSplitter(unittest.TestCase):
506 ls = utils.LineSplitter(lines.append)
507 ls.write("Hello World\n")
508 self.assertEqual(lines, [])
509 ls.write("Foo\n Bar\r\n ")
512 self.assertEqual(lines, [])
514 self.assertEqual(lines, ["Hello World", "Foo", " Bar"])
516 self.assertEqual(lines, ["Hello World", "Foo", " Bar", " BazMoo"])
518 def _testExtra(self, line, all_lines, p1, p2):
519 self.assertEqual(p1, 999)
520 self.assertEqual(p2, "extra")
521 all_lines.append(line)
523 def testExtraArgsNoFlush(self):
525 ls = utils.LineSplitter(self._testExtra, lines, 999, "extra")
526 ls.write("\n\nHello World\n")
527 ls.write("Foo\n Bar\r\n ")
530 ls.write("Moo\n\nx\n")
531 self.assertEqual(lines, [])
533 self.assertEqual(lines, ["", "", "Hello World", "Foo", " Bar", " BazMoo",
537 class TestIsValidShellParam(unittest.TestCase):
543 self.assertEqual(utils.IsValidShellParam(val), result)
546 class TestBuildShellCmd(unittest.TestCase):
548 self.assertRaises(errors.ProgrammerError, utils.BuildShellCmd,
550 self.assertEqual(utils.BuildShellCmd("ls %s", "ab"), "ls ab")
553 class TestOrdinal(unittest.TestCase):
556 0: "0th", 1: "1st", 2: "2nd", 3: "3rd", 4: "4th", 5: "5th", 6: "6th",
557 7: "7th", 8: "8th", 9: "9th", 10: "10th", 11: "11th", 12: "12th",
558 13: "13th", 14: "14th", 15: "15th", 16: "16th", 17: "17th",
559 18: "18th", 19: "19th", 20: "20th", 21: "21st", 25: "25th", 30: "30th",
560 32: "32nd", 40: "40th", 50: "50th", 55: "55th", 60: "60th", 62: "62nd",
561 70: "70th", 80: "80th", 83: "83rd", 90: "90th", 91: "91st",
562 582: "582nd", 999: "999th",
565 for value, ordinal in checks.items():
566 self.assertEqual(utils.FormatOrdinal(value), ordinal)
569 class TestTruncate(unittest.TestCase):
570 def _Test(self, text, length):
571 result = utils.Truncate(text, length)
572 self.assertTrue(len(result) <= length)
576 self.assertEqual(self._Test("", 80), "")
577 self.assertEqual(self._Test("abc", 4), "abc")
578 self.assertEqual(self._Test("Hello World", 80), "Hello World")
579 self.assertEqual(self._Test("Hello World", 4), "H...")
580 self.assertEqual(self._Test("Hello World", 5), "He...")
582 for i in [4, 10, 100]:
583 data = i * "FooBarBaz"
584 self.assertEqual(self._Test(data, len(data)), data)
586 for (length, exp) in [(8, u"T\u00e4st\u2026xyz"), (7, u"T\u00e4st...")]:
587 self.assertEqual(self._Test(u"T\u00e4st\u2026xyz", length), exp)
589 self.assertEqual(self._Test(range(100), 20), "[0, 1, 2, 3, 4, 5...")
593 self.assertRaises(AssertionError, utils.Truncate, "", i)
596 class TestFilterEmptyLinesAndComments(unittest.TestCase):
598 self.assertEqual(utils.FilterEmptyLinesAndComments(""), [])
599 self.assertEqual(utils.FilterEmptyLinesAndComments("\n"), [])
600 self.assertEqual(utils.FilterEmptyLinesAndComments("\n" * 100), [])
601 self.assertEqual(utils.FilterEmptyLinesAndComments("\n \n\t \n"), [])
617 self.assertEqual(utils.FilterEmptyLinesAndComments(text), [
628 if __name__ == "__main__":
629 testutils.GanetiTestProgram()