locking: Change locking order, move NAL after instances
[ganeti-local] / test / ganeti.utils.text_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2011 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """Script for testing ganeti.utils.text"""
23
24 import re
25 import string
26 import time
27 import unittest
28 import os
29
30 from cStringIO import StringIO
31
32 from ganeti import constants
33 from ganeti import utils
34 from ganeti import errors
35
36 import testutils
37
38
39 class TestMatchNameComponent(unittest.TestCase):
40   """Test case for the MatchNameComponent function"""
41
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)
46
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])
52
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)
58
59   def testFullMatch(self):
60     """Test that a full match is returned correctly"""
61     key1 = "test1"
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)
66
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),
72                      "test2.example.net")
73     self.assertEqual(utils.MatchNameComponent("Test2", mlist,
74                                               case_sensitive=False),
75                      "test2.example.net")
76     self.assertEqual(utils.MatchNameComponent("teSt2", mlist,
77                                               case_sensitive=False),
78                      "test2.example.net")
79     self.assertEqual(utils.MatchNameComponent("TeSt2", mlist,
80                                               case_sensitive=False),
81                      "test2.example.net")
82
83   def testCaseInsensitiveFullMatch(self):
84     mlist = ["ts1.ex", "ts1.ex.org", "ts2.ex", "Ts2.ex"]
85
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),
89                      None)
90     self.assertEqual(utils.MatchNameComponent("Ts1.ex", mlist,
91                                               case_sensitive=False),
92                      "ts1.ex")
93     self.assertEqual(utils.MatchNameComponent("ts1.ex", mlist,
94                                               case_sensitive=False),
95                      "ts1.ex")
96
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),
100                      "ts2.ex")
101     self.assertEqual(utils.MatchNameComponent("Ts2.ex", mlist,
102                                               case_sensitive=False),
103                      "Ts2.ex")
104     self.assertEqual(utils.MatchNameComponent("TS2.ex", mlist,
105                                               case_sensitive=False),
106                      None)
107
108
109 class TestDnsNameGlobPattern(unittest.TestCase):
110   def setUp(self):
111     self.names = [
112       "node1.example.com",
113       "node2-0.example.com",
114       "node2-1.example.com",
115       "node1.example.net",
116       "web1.example.com",
117       "web2.example.com",
118       "sub.site.example.com",
119       ]
120
121   def _Test(self, pattern):
122     re_pat = utils.DnsNameGlobPattern(pattern)
123
124     return filter(re.compile(re_pat).match, self.names)
125
126   def test(self):
127     for pattern in ["xyz", "node", " ", "example.net", "x*.example.*",
128                     "x*.example.com"]:
129       self.assertEqual(self._Test(pattern), [])
130
131     for pattern in ["*", "???*"]:
132       self.assertEqual(self._Test(pattern), self.names)
133
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"])
137
138     for pattern in ["*.*.*.*", "???", "*.site"]:
139       self.assertEqual(self._Test(pattern), ["sub.site.example.com"])
140
141     self.assertEqual(self._Test("node1"), [
142       "node1.example.com",
143       "node1.example.net",
144       ])
145     self.assertEqual(self._Test("node?*.example.*"), [
146       "node1.example.com",
147       "node2-0.example.com",
148       "node2-1.example.com",
149       "node1.example.net",
150       ])
151     self.assertEqual(self._Test("*-?"), [
152       "node2-0.example.com",
153       "node2-1.example.com",
154       ])
155     self.assertEqual(self._Test("node2-?.example.com"), [
156       "node2-0.example.com",
157       "node2-1.example.com",
158       ])
159
160
161 class TestFormatUnit(unittest.TestCase):
162   """Test case for the FormatUnit function"""
163
164   def testMiB(self):
165     self.assertEqual(utils.FormatUnit(1, "h"), "1M")
166     self.assertEqual(utils.FormatUnit(100, "h"), "100M")
167     self.assertEqual(utils.FormatUnit(1023, "h"), "1023M")
168
169     self.assertEqual(utils.FormatUnit(1, "m"), "1")
170     self.assertEqual(utils.FormatUnit(100, "m"), "100")
171     self.assertEqual(utils.FormatUnit(1023, "m"), "1023")
172
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")
177
178   def testGiB(self):
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")
183
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")
188
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")
192
193   def testTiB(self):
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")
197
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")
201
202   def testErrors(self):
203     self.assertRaises(errors.ProgrammerError, utils.FormatUnit, 1, "a")
204
205
206 class TestParseUnit(unittest.TestCase):
207   """Test case for the ParseUnit function"""
208
209   SCALES = (("", 1),
210             ("M", 1), ("G", 1024), ("T", 1024 * 1024),
211             ("MB", 1), ("GB", 1024), ("TB", 1024 * 1024),
212             ("MiB", 1), ("GiB", 1024), ("TiB", 1024 * 1024))
213
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)
219
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)
227
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)
241
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)),
247                            1024 * scale)
248
249   def testInvalidInput(self):
250     for sep in ("-", "_", ",", "a"):
251       for suffix, _ in self.SCALES:
252         self.assertRaises(errors.UnitParseError, utils.ParseUnit,
253                           "1" + sep + suffix)
254
255     for suffix, _ in self.SCALES:
256       self.assertRaises(errors.UnitParseError, utils.ParseUnit,
257                         "1,3" + suffix)
258
259
260 class TestShellQuoting(unittest.TestCase):
261   """Test case for shell quoting functions"""
262
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'")
269
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")
274
275
276 class TestShellWriter(unittest.TestCase):
277   def test(self):
278     buf = StringIO()
279     sw = utils.ShellWriter(buf)
280     sw.Write("#!/bin/bash")
281     sw.Write("if true; then")
282     sw.IncIndent()
283     try:
284       sw.Write("echo true")
285
286       sw.Write("for i in 1 2 3")
287       sw.Write("do")
288       sw.IncIndent()
289       try:
290         self.assertEqual(sw._indent, 2)
291         sw.Write("date")
292       finally:
293         sw.DecIndent()
294       sw.Write("done")
295     finally:
296       sw.DecIndent()
297     sw.Write("echo %s", utils.ShellQuote("Hello World"))
298     sw.Write("exit 0")
299
300     self.assertEqual(sw._indent, 0)
301
302     output = buf.getvalue()
303
304     self.assert_(output.endswith("\n"))
305
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'")
311
312   def testEmpty(self):
313     buf = StringIO()
314     sw = utils.ShellWriter(buf)
315     sw = None
316     self.assertEqual(buf.getvalue(), "")
317
318   def testEmptyNoIndent(self):
319     buf = StringIO()
320     sw = utils.ShellWriter(buf, indent=False)
321     sw = None
322     self.assertEqual(buf.getvalue(), "")
323
324   @classmethod
325   def _AddLevel(cls, sw, level):
326     if level == 6:
327       return
328
329     sw.IncIndent()
330     try:
331       # Add empty line, it should not be indented
332       sw.Write("")
333       sw.Write(str(level))
334       cls._AddLevel(sw, level + 1)
335     finally:
336       sw.DecIndent()
337
338   def testEmptyLines(self):
339     buf = StringIO()
340     sw = utils.ShellWriter(buf)
341
342     self._AddLevel(sw, 1)
343
344     self.assertEqual(buf.getvalue(),
345                      "".join("\n%s%s\n" % (i * "  ", i) for i in range(1, 6)))
346
347   def testEmptyLinesNoIndent(self):
348     buf = StringIO()
349     sw = utils.ShellWriter(buf, indent=False)
350
351     self._AddLevel(sw, 1)
352
353     self.assertEqual(buf.getvalue(),
354                      "".join("\n%s\n" % i for i in range(1, 6)))
355
356
357 class TestNormalizeAndValidateMac(unittest.TestCase):
358   def testInvalid(self):
359     self.assertRaises(errors.OpPrereqError,
360                       utils.NormalizeAndValidateMac, "xxx")
361
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())
365
366
367 class TestSafeEncode(unittest.TestCase):
368   """Test case for SafeEncode"""
369
370   def testAscii(self):
371     for txt in [string.digits, string.letters, string.punctuation]:
372       self.failUnlessEqual(txt, utils.SafeEncode(txt))
373
374   def testDoubleEncode(self):
375     for i in range(255):
376       txt = utils.SafeEncode(chr(i))
377       self.failUnlessEqual(txt, utils.SafeEncode(txt))
378
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))
384
385
386 class TestUnescapeAndSplit(unittest.TestCase):
387   """Testing case for UnescapeAndSplit"""
388
389   def setUp(self):
390     # testing more that one separator for regexp safety
391     self._seps = [",", "+", ".", ":"]
392
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)
397
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)
403
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)
409
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)
415
416   def testEscapeAtEnd(self):
417     for sep in self._seps:
418       self.assertEqual(utils.UnescapeAndSplit("\\", sep=sep), ["\\"])
419
420       a = ["a", "b\\", "c"]
421       b = ["a", "b" + sep + "c\\"]
422       self.assertEqual(utils.UnescapeAndSplit("%s\\" % sep.join(a), sep=sep), b)
423
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)
427
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)
433
434
435 class TestCommaJoin(unittest.TestCase):
436   def test(self):
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]),
442                      "Hello, World, 99")
443
444
445 class TestFormatTime(unittest.TestCase):
446   """Testing case for FormatTime"""
447
448   @staticmethod
449   def _TestInProcess(tz, timestamp, usecs, expected):
450     os.environ["TZ"] = tz
451     time.tzset()
452     return utils.FormatTime(timestamp, usecs=usecs) == expected
453
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))
457
458   def test(self):
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")
468
469   def testNone(self):
470     self.failUnlessEqual(utils.FormatTime(None), "N/A")
471
472   def testInvalid(self):
473     self.failUnlessEqual(utils.FormatTime(()), "N/A")
474
475   def testNow(self):
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()))
480
481
482 class TestFormatSeconds(unittest.TestCase):
483   def test(self):
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")
495
496   def testFloat(self):
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")
501
502
503 class TestLineSplitter(unittest.TestCase):
504   def test(self):
505     lines = []
506     ls = utils.LineSplitter(lines.append)
507     ls.write("Hello World\n")
508     self.assertEqual(lines, [])
509     ls.write("Foo\n Bar\r\n ")
510     ls.write("Baz")
511     ls.write("Moo")
512     self.assertEqual(lines, [])
513     ls.flush()
514     self.assertEqual(lines, ["Hello World", "Foo", " Bar"])
515     ls.close()
516     self.assertEqual(lines, ["Hello World", "Foo", " Bar", " BazMoo"])
517
518   def _testExtra(self, line, all_lines, p1, p2):
519     self.assertEqual(p1, 999)
520     self.assertEqual(p2, "extra")
521     all_lines.append(line)
522
523   def testExtraArgsNoFlush(self):
524     lines = []
525     ls = utils.LineSplitter(self._testExtra, lines, 999, "extra")
526     ls.write("\n\nHello World\n")
527     ls.write("Foo\n Bar\r\n ")
528     ls.write("")
529     ls.write("Baz")
530     ls.write("Moo\n\nx\n")
531     self.assertEqual(lines, [])
532     ls.close()
533     self.assertEqual(lines, ["", "", "Hello World", "Foo", " Bar", " BazMoo",
534                              "", "x"])
535
536
537 class TestIsValidShellParam(unittest.TestCase):
538   def test(self):
539     for val, result in [
540       ("abc", True),
541       ("ab;cd", False),
542       ]:
543       self.assertEqual(utils.IsValidShellParam(val), result)
544
545
546 class TestBuildShellCmd(unittest.TestCase):
547   def test(self):
548     self.assertRaises(errors.ProgrammerError, utils.BuildShellCmd,
549                       "ls %s", "ab;cd")
550     self.assertEqual(utils.BuildShellCmd("ls %s", "ab"), "ls ab")
551
552
553 class TestOrdinal(unittest.TestCase):
554   def test(self):
555     checks = {
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",
563       }
564
565     for value, ordinal in checks.items():
566       self.assertEqual(utils.FormatOrdinal(value), ordinal)
567
568
569 class TestTruncate(unittest.TestCase):
570   def _Test(self, text, length):
571     result = utils.Truncate(text, length)
572     self.assertTrue(len(result) <= length)
573     return result
574
575   def test(self):
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...")
581
582     for i in [4, 10, 100]:
583       data = i * "FooBarBaz"
584       self.assertEqual(self._Test(data, len(data)), data)
585
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)
588
589     self.assertEqual(self._Test(range(100), 20), "[0, 1, 2, 3, 4, 5...")
590
591   def testError(self):
592     for i in range(4):
593       self.assertRaises(AssertionError, utils.Truncate, "", i)
594
595
596 class TestFilterEmptyLinesAndComments(unittest.TestCase):
597   def testEmpty(self):
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"), [])
602
603   def test(self):
604     text = """
605       This
606         is
607       # with comments
608           a
609             test
610             # in
611             #
612             saying
613       ...#...
614         # multiple places
615         Hello World!
616       """
617     self.assertEqual(utils.FilterEmptyLinesAndComments(text), [
618       "This",
619       "is",
620       "a",
621       "test",
622       "saying",
623       "...#...",
624       "Hello World!",
625       ])
626
627
628 if __name__ == "__main__":
629   testutils.GanetiTestProgram()