Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.utils_unittest.py @ c9c4f19e

History | View | Annotate | Download (19.9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Script for unittesting the utils module"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import unittest
25 a8083063 Iustin Pop
import os
26 a8083063 Iustin Pop
import time
27 a8083063 Iustin Pop
import tempfile
28 a8083063 Iustin Pop
import os.path
29 320b4e2d Alexander Schreiber
import os
30 a8083063 Iustin Pop
import md5
31 2c30e9d7 Alexander Schreiber
import socket
32 eedbda4b Michael Hanselmann
import shutil
33 59072e7e Michael Hanselmann
import re
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
import ganeti
36 c9c4f19e Michael Hanselmann
import testutils
37 16abfbc2 Alexander Schreiber
from ganeti import constants
38 59072e7e Michael Hanselmann
from ganeti import utils
39 a8083063 Iustin Pop
from ganeti.utils import IsProcessAlive, Lock, Unlock, RunCmd, \
40 a8083063 Iustin Pop
     RemoveFile, CheckDict, MatchNameComponent, FormatUnit, \
41 a8083063 Iustin Pop
     ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \
42 899d2a81 Michael Hanselmann
     ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
43 9440aeab Michael Hanselmann
     SetEtcHostsEntry, RemoveEtcHostsEntry
44 a8083063 Iustin Pop
from ganeti.errors import LockError, UnitParseError
45 a8083063 Iustin Pop
46 2c30e9d7 Alexander Schreiber
47 a8083063 Iustin Pop
class TestIsProcessAlive(unittest.TestCase):
48 a8083063 Iustin Pop
  """Testing case for IsProcessAlive"""
49 a8083063 Iustin Pop
  def setUp(self):
50 a8083063 Iustin Pop
    # create a zombie and a (hopefully) non-existing process id
51 a8083063 Iustin Pop
    self.pid_zombie = os.fork()
52 a8083063 Iustin Pop
    if self.pid_zombie == 0:
53 a8083063 Iustin Pop
      os._exit(0)
54 a8083063 Iustin Pop
    elif self.pid_zombie < 0:
55 a8083063 Iustin Pop
      raise SystemError("can't fork")
56 a8083063 Iustin Pop
    self.pid_non_existing = os.fork()
57 a8083063 Iustin Pop
    if self.pid_non_existing == 0:
58 a8083063 Iustin Pop
      os._exit(0)
59 a8083063 Iustin Pop
    elif self.pid_non_existing > 0:
60 a8083063 Iustin Pop
      os.waitpid(self.pid_non_existing, 0)
61 a8083063 Iustin Pop
    else:
62 a8083063 Iustin Pop
      raise SystemError("can't fork")
63 a8083063 Iustin Pop
64 a8083063 Iustin Pop
65 a8083063 Iustin Pop
  def testExists(self):
66 a8083063 Iustin Pop
    mypid = os.getpid()
67 a8083063 Iustin Pop
    self.assert_(IsProcessAlive(mypid),
68 a8083063 Iustin Pop
                 "can't find myself running")
69 a8083063 Iustin Pop
70 a8083063 Iustin Pop
  def testZombie(self):
71 a8083063 Iustin Pop
    self.assert_(not IsProcessAlive(self.pid_zombie),
72 a8083063 Iustin Pop
                 "zombie not detected as zombie")
73 a8083063 Iustin Pop
74 a8083063 Iustin Pop
75 a8083063 Iustin Pop
  def testNotExisting(self):
76 a8083063 Iustin Pop
    self.assert_(not IsProcessAlive(self.pid_non_existing),
77 a8083063 Iustin Pop
                 "noexisting process detected")
78 a8083063 Iustin Pop
79 a8083063 Iustin Pop
80 a8083063 Iustin Pop
class TestLocking(unittest.TestCase):
81 a8083063 Iustin Pop
  """Testing case for the Lock/Unlock functions"""
82 320b4e2d Alexander Schreiber
83 320b4e2d Alexander Schreiber
  def setUp(self):
84 320b4e2d Alexander Schreiber
    lock_dir = tempfile.mkdtemp(prefix="ganeti.unittest.",
85 320b4e2d Alexander Schreiber
                                suffix=".locking")
86 320b4e2d Alexander Schreiber
    self.old_lock_dir = constants.LOCK_DIR
87 320b4e2d Alexander Schreiber
    constants.LOCK_DIR = lock_dir
88 320b4e2d Alexander Schreiber
89 320b4e2d Alexander Schreiber
  def tearDown(self):
90 320b4e2d Alexander Schreiber
    try:
91 320b4e2d Alexander Schreiber
      ganeti.utils.Unlock("unittest")
92 320b4e2d Alexander Schreiber
    except LockError:
93 320b4e2d Alexander Schreiber
      pass
94 320b4e2d Alexander Schreiber
    shutil.rmtree(constants.LOCK_DIR, ignore_errors=True)
95 320b4e2d Alexander Schreiber
    constants.LOCK_DIR = self.old_lock_dir
96 320b4e2d Alexander Schreiber
97 a8083063 Iustin Pop
  def clean_lock(self, name):
98 a8083063 Iustin Pop
    try:
99 a8083063 Iustin Pop
      ganeti.utils.Unlock("unittest")
100 a8083063 Iustin Pop
    except LockError:
101 a8083063 Iustin Pop
      pass
102 a8083063 Iustin Pop
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
  def testLock(self):
105 a8083063 Iustin Pop
    self.clean_lock("unittest")
106 a8083063 Iustin Pop
    self.assertEqual(None, Lock("unittest"))
107 a8083063 Iustin Pop
108 a8083063 Iustin Pop
109 a8083063 Iustin Pop
  def testUnlock(self):
110 a8083063 Iustin Pop
    self.clean_lock("unittest")
111 a8083063 Iustin Pop
    ganeti.utils.Lock("unittest")
112 a8083063 Iustin Pop
    self.assertEqual(None, Unlock("unittest"))
113 a8083063 Iustin Pop
114 a8083063 Iustin Pop
  def testDoubleLock(self):
115 a8083063 Iustin Pop
    self.clean_lock("unittest")
116 a8083063 Iustin Pop
    ganeti.utils.Lock("unittest")
117 a8083063 Iustin Pop
    self.assertRaises(LockError, Lock, "unittest")
118 a8083063 Iustin Pop
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
class TestRunCmd(unittest.TestCase):
121 a8083063 Iustin Pop
  """Testing case for the RunCmd function"""
122 a8083063 Iustin Pop
123 a8083063 Iustin Pop
  def setUp(self):
124 a8083063 Iustin Pop
    self.magic = time.ctime() + " ganeti test"
125 a8083063 Iustin Pop
126 a8083063 Iustin Pop
  def testOk(self):
127 31ee599c Michael Hanselmann
    """Test successful exit code"""
128 a8083063 Iustin Pop
    result = RunCmd("/bin/sh -c 'exit 0'")
129 a8083063 Iustin Pop
    self.assertEqual(result.exit_code, 0)
130 a8083063 Iustin Pop
131 a8083063 Iustin Pop
  def testFail(self):
132 a8083063 Iustin Pop
    """Test fail exit code"""
133 a8083063 Iustin Pop
    result = RunCmd("/bin/sh -c 'exit 1'")
134 a8083063 Iustin Pop
    self.assertEqual(result.exit_code, 1)
135 a8083063 Iustin Pop
136 a8083063 Iustin Pop
137 a8083063 Iustin Pop
  def testStdout(self):
138 a8083063 Iustin Pop
    """Test standard output"""
139 a8083063 Iustin Pop
    cmd = 'echo -n "%s"' % self.magic
140 a8083063 Iustin Pop
    result = RunCmd("/bin/sh -c '%s'" % cmd)
141 a8083063 Iustin Pop
    self.assertEqual(result.stdout, self.magic)
142 a8083063 Iustin Pop
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  def testStderr(self):
145 a8083063 Iustin Pop
    """Test standard error"""
146 a8083063 Iustin Pop
    cmd = 'echo -n "%s"' % self.magic
147 a8083063 Iustin Pop
    result = RunCmd("/bin/sh -c '%s' 1>&2" % cmd)
148 a8083063 Iustin Pop
    self.assertEqual(result.stderr, self.magic)
149 a8083063 Iustin Pop
150 a8083063 Iustin Pop
151 a8083063 Iustin Pop
  def testCombined(self):
152 a8083063 Iustin Pop
    """Test combined output"""
153 a8083063 Iustin Pop
    cmd = 'echo -n "A%s"; echo -n "B%s" 1>&2' % (self.magic, self.magic)
154 a8083063 Iustin Pop
    result = RunCmd("/bin/sh -c '%s'" % cmd)
155 a8083063 Iustin Pop
    self.assertEqual(result.output, "A" + self.magic + "B" + self.magic)
156 a8083063 Iustin Pop
157 a8083063 Iustin Pop
  def testSignal(self):
158 a8083063 Iustin Pop
    """Test standard error"""
159 a8083063 Iustin Pop
    result = RunCmd("/bin/sh -c 'kill -15 $$'")
160 a8083063 Iustin Pop
    self.assertEqual(result.signal, 15)
161 a8083063 Iustin Pop
162 7fcf849f Iustin Pop
  def testListRun(self):
163 7fcf849f Iustin Pop
    """Test list runs"""
164 7fcf849f Iustin Pop
    result = RunCmd(["true"])
165 7fcf849f Iustin Pop
    self.assertEqual(result.signal, None)
166 7fcf849f Iustin Pop
    self.assertEqual(result.exit_code, 0)
167 7fcf849f Iustin Pop
    result = RunCmd(["/bin/sh", "-c", "exit 1"])
168 7fcf849f Iustin Pop
    self.assertEqual(result.signal, None)
169 7fcf849f Iustin Pop
    self.assertEqual(result.exit_code, 1)
170 7fcf849f Iustin Pop
    result = RunCmd(["echo", "-n", self.magic])
171 7fcf849f Iustin Pop
    self.assertEqual(result.signal, None)
172 7fcf849f Iustin Pop
    self.assertEqual(result.exit_code, 0)
173 7fcf849f Iustin Pop
    self.assertEqual(result.stdout, self.magic)
174 7fcf849f Iustin Pop
175 f6441c7c Iustin Pop
  def testLang(self):
176 f6441c7c Iustin Pop
    """Test locale environment"""
177 23f41a3e Michael Hanselmann
    old_env = os.environ.copy()
178 23f41a3e Michael Hanselmann
    try:
179 23f41a3e Michael Hanselmann
      os.environ["LANG"] = "en_US.UTF-8"
180 23f41a3e Michael Hanselmann
      os.environ["LC_ALL"] = "en_US.UTF-8"
181 23f41a3e Michael Hanselmann
      result = RunCmd(["locale"])
182 23f41a3e Michael Hanselmann
      for line in result.output.splitlines():
183 23f41a3e Michael Hanselmann
        key, value = line.split("=", 1)
184 23f41a3e Michael Hanselmann
        # Ignore these variables, they're overridden by LC_ALL
185 23f41a3e Michael Hanselmann
        if key == "LANG" or key == "LANGUAGE":
186 23f41a3e Michael Hanselmann
          continue
187 23f41a3e Michael Hanselmann
        self.failIf(value and value != "C" and value != '"C"',
188 23f41a3e Michael Hanselmann
            "Variable %s is set to the invalid value '%s'" % (key, value))
189 23f41a3e Michael Hanselmann
    finally:
190 23f41a3e Michael Hanselmann
      os.environ = old_env
191 f6441c7c Iustin Pop
192 a8083063 Iustin Pop
193 a8083063 Iustin Pop
class TestRemoveFile(unittest.TestCase):
194 a8083063 Iustin Pop
  """Test case for the RemoveFile function"""
195 a8083063 Iustin Pop
196 a8083063 Iustin Pop
  def setUp(self):
197 a8083063 Iustin Pop
    """Create a temp dir and file for each case"""
198 a8083063 Iustin Pop
    self.tmpdir = tempfile.mkdtemp('', 'ganeti-unittest-')
199 a8083063 Iustin Pop
    fd, self.tmpfile = tempfile.mkstemp('', '', self.tmpdir)
200 a8083063 Iustin Pop
    os.close(fd)
201 a8083063 Iustin Pop
202 a8083063 Iustin Pop
  def tearDown(self):
203 a8083063 Iustin Pop
    if os.path.exists(self.tmpfile):
204 a8083063 Iustin Pop
      os.unlink(self.tmpfile)
205 a8083063 Iustin Pop
    os.rmdir(self.tmpdir)
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
208 a8083063 Iustin Pop
  def testIgnoreDirs(self):
209 a8083063 Iustin Pop
    """Test that RemoveFile() ignores directories"""
210 a8083063 Iustin Pop
    self.assertEqual(None, RemoveFile(self.tmpdir))
211 a8083063 Iustin Pop
212 a8083063 Iustin Pop
213 a8083063 Iustin Pop
  def testIgnoreNotExisting(self):
214 a8083063 Iustin Pop
    """Test that RemoveFile() ignores non-existing files"""
215 a8083063 Iustin Pop
    RemoveFile(self.tmpfile)
216 a8083063 Iustin Pop
    RemoveFile(self.tmpfile)
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
219 a8083063 Iustin Pop
  def testRemoveFile(self):
220 a8083063 Iustin Pop
    """Test that RemoveFile does remove a file"""
221 a8083063 Iustin Pop
    RemoveFile(self.tmpfile)
222 a8083063 Iustin Pop
    if os.path.exists(self.tmpfile):
223 a8083063 Iustin Pop
      self.fail("File '%s' not removed" % self.tmpfile)
224 a8083063 Iustin Pop
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
  def testRemoveSymlink(self):
227 a8083063 Iustin Pop
    """Test that RemoveFile does remove symlinks"""
228 a8083063 Iustin Pop
    symlink = self.tmpdir + "/symlink"
229 a8083063 Iustin Pop
    os.symlink("no-such-file", symlink)
230 a8083063 Iustin Pop
    RemoveFile(symlink)
231 a8083063 Iustin Pop
    if os.path.exists(symlink):
232 a8083063 Iustin Pop
      self.fail("File '%s' not removed" % symlink)
233 a8083063 Iustin Pop
    os.symlink(self.tmpfile, symlink)
234 a8083063 Iustin Pop
    RemoveFile(symlink)
235 a8083063 Iustin Pop
    if os.path.exists(symlink):
236 a8083063 Iustin Pop
      self.fail("File '%s' not removed" % symlink)
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
239 a8083063 Iustin Pop
class TestCheckdict(unittest.TestCase):
240 a8083063 Iustin Pop
  """Test case for the CheckDict function"""
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
  def testAdd(self):
243 a8083063 Iustin Pop
    """Test that CheckDict adds a missing key with the correct value"""
244 a8083063 Iustin Pop
245 a8083063 Iustin Pop
    tgt = {'a':1}
246 a8083063 Iustin Pop
    tmpl = {'b': 2}
247 a8083063 Iustin Pop
    CheckDict(tgt, tmpl)
248 a8083063 Iustin Pop
    if 'b' not in tgt or tgt['b'] != 2:
249 a8083063 Iustin Pop
      self.fail("Failed to update dict")
250 a8083063 Iustin Pop
251 a8083063 Iustin Pop
252 a8083063 Iustin Pop
  def testNoUpdate(self):
253 a8083063 Iustin Pop
    """Test that CheckDict does not overwrite an existing key"""
254 a8083063 Iustin Pop
    tgt = {'a':1, 'b': 3}
255 a8083063 Iustin Pop
    tmpl = {'b': 2}
256 a8083063 Iustin Pop
    CheckDict(tgt, tmpl)
257 a8083063 Iustin Pop
    self.failUnlessEqual(tgt['b'], 3)
258 a8083063 Iustin Pop
259 a8083063 Iustin Pop
260 a8083063 Iustin Pop
class TestMatchNameComponent(unittest.TestCase):
261 a8083063 Iustin Pop
  """Test case for the MatchNameComponent function"""
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
  def testEmptyList(self):
264 a8083063 Iustin Pop
    """Test that there is no match against an empty list"""
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
    self.failUnlessEqual(MatchNameComponent("", []), None)
267 a8083063 Iustin Pop
    self.failUnlessEqual(MatchNameComponent("test", []), None)
268 a8083063 Iustin Pop
269 a8083063 Iustin Pop
  def testSingleMatch(self):
270 a8083063 Iustin Pop
    """Test that a single match is performed correctly"""
271 a8083063 Iustin Pop
    mlist = ["test1.example.com", "test2.example.com", "test3.example.com"]
272 a8083063 Iustin Pop
    for key in "test2", "test2.example", "test2.example.com":
273 a8083063 Iustin Pop
      self.failUnlessEqual(MatchNameComponent(key, mlist), mlist[1])
274 a8083063 Iustin Pop
275 a8083063 Iustin Pop
  def testMultipleMatches(self):
276 a8083063 Iustin Pop
    """Test that a multiple match is returned as None"""
277 a8083063 Iustin Pop
    mlist = ["test1.example.com", "test1.example.org", "test1.example.net"]
278 a8083063 Iustin Pop
    for key in "test1", "test1.example":
279 a8083063 Iustin Pop
      self.failUnlessEqual(MatchNameComponent(key, mlist), None)
280 a8083063 Iustin Pop
281 a8083063 Iustin Pop
282 a8083063 Iustin Pop
class TestFormatUnit(unittest.TestCase):
283 a8083063 Iustin Pop
  """Test case for the FormatUnit function"""
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
  def testMiB(self):
286 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(1), '1M')
287 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(100), '100M')
288 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(1023), '1023M')
289 a8083063 Iustin Pop
290 a8083063 Iustin Pop
  def testGiB(self):
291 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(1024), '1.0G')
292 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(1536), '1.5G')
293 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(17133), '16.7G')
294 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(1024 * 1024 - 1), '1024.0G')
295 a8083063 Iustin Pop
296 a8083063 Iustin Pop
  def testTiB(self):
297 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(1024 * 1024), '1.0T')
298 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(5120 * 1024), '5.0T')
299 a8083063 Iustin Pop
    self.assertEqual(FormatUnit(29829 * 1024), '29.1T')
300 a8083063 Iustin Pop
301 a8083063 Iustin Pop
302 a8083063 Iustin Pop
class TestParseUnit(unittest.TestCase):
303 a8083063 Iustin Pop
  """Test case for the ParseUnit function"""
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
  SCALES = (('', 1),
306 a8083063 Iustin Pop
            ('M', 1), ('G', 1024), ('T', 1024 * 1024),
307 a8083063 Iustin Pop
            ('MB', 1), ('GB', 1024), ('TB', 1024 * 1024),
308 a8083063 Iustin Pop
            ('MiB', 1), ('GiB', 1024), ('TiB', 1024 * 1024))
309 a8083063 Iustin Pop
310 a8083063 Iustin Pop
  def testRounding(self):
311 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('0'), 0)
312 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('1'), 4)
313 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('2'), 4)
314 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('3'), 4)
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('124'), 124)
317 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('125'), 128)
318 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('126'), 128)
319 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('127'), 128)
320 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('128'), 128)
321 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('129'), 132)
322 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('130'), 132)
323 a8083063 Iustin Pop
324 a8083063 Iustin Pop
  def testFloating(self):
325 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('0'), 0)
326 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('0.5'), 4)
327 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('1.75'), 4)
328 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('1.99'), 4)
329 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('2.00'), 4)
330 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('2.01'), 4)
331 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('3.99'), 4)
332 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('4.00'), 4)
333 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('4.01'), 8)
334 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('1.5G'), 1536)
335 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('1.8G'), 1844)
336 a8083063 Iustin Pop
    self.assertEqual(ParseUnit('8.28T'), 8682212)
337 a8083063 Iustin Pop
338 a8083063 Iustin Pop
  def testSuffixes(self):
339 a8083063 Iustin Pop
    for sep in ('', ' ', '   ', "\t", "\t "):
340 a8083063 Iustin Pop
      for suffix, scale in TestParseUnit.SCALES:
341 a8083063 Iustin Pop
        for func in (lambda x: x, str.lower, str.upper):
342 667479d5 Michael Hanselmann
          self.assertEqual(ParseUnit('1024' + sep + func(suffix)),
343 667479d5 Michael Hanselmann
                           1024 * scale)
344 a8083063 Iustin Pop
345 a8083063 Iustin Pop
  def testInvalidInput(self):
346 a8083063 Iustin Pop
    for sep in ('-', '_', ',', 'a'):
347 a8083063 Iustin Pop
      for suffix, _ in TestParseUnit.SCALES:
348 a8083063 Iustin Pop
        self.assertRaises(UnitParseError, ParseUnit, '1' + sep + suffix)
349 a8083063 Iustin Pop
350 a8083063 Iustin Pop
    for suffix, _ in TestParseUnit.SCALES:
351 a8083063 Iustin Pop
      self.assertRaises(UnitParseError, ParseUnit, '1,3' + suffix)
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
354 c9c4f19e Michael Hanselmann
class TestSshKeys(testutils.GanetiTestCase):
355 a8083063 Iustin Pop
  """Test case for the AddAuthorizedKey function"""
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
  KEY_A = 'ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@key-a'
358 a8083063 Iustin Pop
  KEY_B = ('command="/usr/bin/fooserver -t --verbose",from="1.2.3.4" '
359 a8083063 Iustin Pop
           'ssh-dss AAAAB3NzaC1w520smc01ms0jfJs22 root@key-b')
360 a8083063 Iustin Pop
361 ebe8ef17 Michael Hanselmann
  def setUp(self):
362 ebe8ef17 Michael Hanselmann
    (fd, self.tmpname) = tempfile.mkstemp(prefix='ganeti-test')
363 a8083063 Iustin Pop
    try:
364 ebe8ef17 Michael Hanselmann
      handle = os.fdopen(fd, 'w')
365 ebe8ef17 Michael Hanselmann
      try:
366 ebe8ef17 Michael Hanselmann
        handle.write("%s\n" % TestSshKeys.KEY_A)
367 ebe8ef17 Michael Hanselmann
        handle.write("%s\n" % TestSshKeys.KEY_B)
368 ebe8ef17 Michael Hanselmann
      finally:
369 ebe8ef17 Michael Hanselmann
        handle.close()
370 ebe8ef17 Michael Hanselmann
    except:
371 ebe8ef17 Michael Hanselmann
      utils.RemoveFile(self.tmpname)
372 ebe8ef17 Michael Hanselmann
      raise
373 a8083063 Iustin Pop
374 ebe8ef17 Michael Hanselmann
  def tearDown(self):
375 ebe8ef17 Michael Hanselmann
    utils.RemoveFile(self.tmpname)
376 ebe8ef17 Michael Hanselmann
    del self.tmpname
377 a8083063 Iustin Pop
378 a8083063 Iustin Pop
  def testAddingNewKey(self):
379 ebe8ef17 Michael Hanselmann
    AddAuthorizedKey(self.tmpname, 'ssh-dss AAAAB3NzaC1kc3MAAACB root@test')
380 a8083063 Iustin Pop
381 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
382 ebe8ef17 Michael Hanselmann
      "ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@key-a\n"
383 ebe8ef17 Michael Hanselmann
      'command="/usr/bin/fooserver -t --verbose",from="1.2.3.4"'
384 ebe8ef17 Michael Hanselmann
      " ssh-dss AAAAB3NzaC1w520smc01ms0jfJs22 root@key-b\n"
385 ebe8ef17 Michael Hanselmann
      "ssh-dss AAAAB3NzaC1kc3MAAACB root@test\n")
386 a8083063 Iustin Pop
387 f89f17a8 Michael Hanselmann
  def testAddingAlmostButNotCompletelyTheSameKey(self):
388 ebe8ef17 Michael Hanselmann
    AddAuthorizedKey(self.tmpname,
389 ebe8ef17 Michael Hanselmann
        'ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@test')
390 ebe8ef17 Michael Hanselmann
391 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
392 ebe8ef17 Michael Hanselmann
      "ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@key-a\n"
393 ebe8ef17 Michael Hanselmann
      'command="/usr/bin/fooserver -t --verbose",from="1.2.3.4"'
394 ebe8ef17 Michael Hanselmann
      " ssh-dss AAAAB3NzaC1w520smc01ms0jfJs22 root@key-b\n"
395 ebe8ef17 Michael Hanselmann
      "ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@test\n")
396 a8083063 Iustin Pop
397 a8083063 Iustin Pop
  def testAddingExistingKeyWithSomeMoreSpaces(self):
398 ebe8ef17 Michael Hanselmann
    AddAuthorizedKey(self.tmpname,
399 ebe8ef17 Michael Hanselmann
        'ssh-dss  AAAAB3NzaC1w5256closdj32mZaQU   root@key-a')
400 a8083063 Iustin Pop
401 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
402 ebe8ef17 Michael Hanselmann
      "ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@key-a\n"
403 ebe8ef17 Michael Hanselmann
      'command="/usr/bin/fooserver -t --verbose",from="1.2.3.4"'
404 ebe8ef17 Michael Hanselmann
      " ssh-dss AAAAB3NzaC1w520smc01ms0jfJs22 root@key-b\n")
405 a8083063 Iustin Pop
406 a8083063 Iustin Pop
  def testRemovingExistingKeyWithSomeMoreSpaces(self):
407 ebe8ef17 Michael Hanselmann
    RemoveAuthorizedKey(self.tmpname,
408 ebe8ef17 Michael Hanselmann
        'ssh-dss  AAAAB3NzaC1w5256closdj32mZaQU   root@key-a')
409 a8083063 Iustin Pop
410 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
411 ebe8ef17 Michael Hanselmann
      'command="/usr/bin/fooserver -t --verbose",from="1.2.3.4"'
412 ebe8ef17 Michael Hanselmann
      " ssh-dss AAAAB3NzaC1w520smc01ms0jfJs22 root@key-b\n")
413 a8083063 Iustin Pop
414 a8083063 Iustin Pop
  def testRemovingNonExistingKey(self):
415 ebe8ef17 Michael Hanselmann
    RemoveAuthorizedKey(self.tmpname,
416 ebe8ef17 Michael Hanselmann
        'ssh-dss  AAAAB3Nsdfj230xxjxJjsjwjsjdjU   root@test')
417 a8083063 Iustin Pop
418 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
419 ebe8ef17 Michael Hanselmann
      "ssh-dss AAAAB3NzaC1w5256closdj32mZaQU root@key-a\n"
420 ebe8ef17 Michael Hanselmann
      'command="/usr/bin/fooserver -t --verbose",from="1.2.3.4"'
421 ebe8ef17 Michael Hanselmann
      " ssh-dss AAAAB3NzaC1w520smc01ms0jfJs22 root@key-b\n")
422 a8083063 Iustin Pop
423 a8083063 Iustin Pop
424 c9c4f19e Michael Hanselmann
class TestEtcHosts(testutils.GanetiTestCase):
425 899d2a81 Michael Hanselmann
  """Test functions modifying /etc/hosts"""
426 899d2a81 Michael Hanselmann
427 ebe8ef17 Michael Hanselmann
  def setUp(self):
428 ebe8ef17 Michael Hanselmann
    (fd, self.tmpname) = tempfile.mkstemp(prefix='ganeti-test')
429 899d2a81 Michael Hanselmann
    try:
430 ebe8ef17 Michael Hanselmann
      handle = os.fdopen(fd, 'w')
431 ebe8ef17 Michael Hanselmann
      try:
432 ebe8ef17 Michael Hanselmann
        handle.write('# This is a test file for /etc/hosts\n')
433 ebe8ef17 Michael Hanselmann
        handle.write('127.0.0.1\tlocalhost\n')
434 ebe8ef17 Michael Hanselmann
        handle.write('192.168.1.1 router gw\n')
435 ebe8ef17 Michael Hanselmann
      finally:
436 ebe8ef17 Michael Hanselmann
        handle.close()
437 ebe8ef17 Michael Hanselmann
    except:
438 ebe8ef17 Michael Hanselmann
      utils.RemoveFile(self.tmpname)
439 ebe8ef17 Michael Hanselmann
      raise
440 899d2a81 Michael Hanselmann
441 ebe8ef17 Michael Hanselmann
  def tearDown(self):
442 ebe8ef17 Michael Hanselmann
    utils.RemoveFile(self.tmpname)
443 ebe8ef17 Michael Hanselmann
    del self.tmpname
444 899d2a81 Michael Hanselmann
445 9440aeab Michael Hanselmann
  def testSettingNewIp(self):
446 ebe8ef17 Michael Hanselmann
    SetEtcHostsEntry(self.tmpname, '1.2.3.4', 'myhost.domain.tld', ['myhost'])
447 899d2a81 Michael Hanselmann
448 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
449 ebe8ef17 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
450 ebe8ef17 Michael Hanselmann
      "127.0.0.1\tlocalhost\n"
451 ebe8ef17 Michael Hanselmann
      "192.168.1.1 router gw\n"
452 ebe8ef17 Michael Hanselmann
      "1.2.3.4\tmyhost.domain.tld myhost\n")
453 899d2a81 Michael Hanselmann
454 9440aeab Michael Hanselmann
  def testSettingExistingIp(self):
455 ebe8ef17 Michael Hanselmann
    SetEtcHostsEntry(self.tmpname, '192.168.1.1', 'myhost.domain.tld',
456 ebe8ef17 Michael Hanselmann
                     ['myhost'])
457 899d2a81 Michael Hanselmann
458 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
459 ebe8ef17 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
460 ebe8ef17 Michael Hanselmann
      "127.0.0.1\tlocalhost\n"
461 ebe8ef17 Michael Hanselmann
      "192.168.1.1\tmyhost.domain.tld myhost\n")
462 899d2a81 Michael Hanselmann
463 7fbb1f65 Michael Hanselmann
  def testSettingDuplicateName(self):
464 7fbb1f65 Michael Hanselmann
    SetEtcHostsEntry(self.tmpname, '1.2.3.4', 'myhost', ['myhost'])
465 7fbb1f65 Michael Hanselmann
466 7fbb1f65 Michael Hanselmann
    self.assertFileContent(self.tmpname,
467 7fbb1f65 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
468 7fbb1f65 Michael Hanselmann
      "127.0.0.1\tlocalhost\n"
469 7fbb1f65 Michael Hanselmann
      "192.168.1.1 router gw\n"
470 7fbb1f65 Michael Hanselmann
      "1.2.3.4\tmyhost\n")
471 7fbb1f65 Michael Hanselmann
472 899d2a81 Michael Hanselmann
  def testRemovingExistingHost(self):
473 ebe8ef17 Michael Hanselmann
    RemoveEtcHostsEntry(self.tmpname, 'router')
474 899d2a81 Michael Hanselmann
475 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
476 ebe8ef17 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
477 ebe8ef17 Michael Hanselmann
      "127.0.0.1\tlocalhost\n"
478 ebe8ef17 Michael Hanselmann
      "192.168.1.1 gw\n")
479 899d2a81 Michael Hanselmann
480 899d2a81 Michael Hanselmann
  def testRemovingSingleExistingHost(self):
481 ebe8ef17 Michael Hanselmann
    RemoveEtcHostsEntry(self.tmpname, 'localhost')
482 899d2a81 Michael Hanselmann
483 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
484 ebe8ef17 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
485 ebe8ef17 Michael Hanselmann
      "192.168.1.1 router gw\n")
486 899d2a81 Michael Hanselmann
487 899d2a81 Michael Hanselmann
  def testRemovingNonExistingHost(self):
488 ebe8ef17 Michael Hanselmann
    RemoveEtcHostsEntry(self.tmpname, 'myhost')
489 899d2a81 Michael Hanselmann
490 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
491 ebe8ef17 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
492 ebe8ef17 Michael Hanselmann
      "127.0.0.1\tlocalhost\n"
493 ebe8ef17 Michael Hanselmann
      "192.168.1.1 router gw\n")
494 899d2a81 Michael Hanselmann
495 9440aeab Michael Hanselmann
  def testRemovingAlias(self):
496 ebe8ef17 Michael Hanselmann
    RemoveEtcHostsEntry(self.tmpname, 'gw')
497 9440aeab Michael Hanselmann
498 ebe8ef17 Michael Hanselmann
    self.assertFileContent(self.tmpname,
499 ebe8ef17 Michael Hanselmann
      "# This is a test file for /etc/hosts\n"
500 ebe8ef17 Michael Hanselmann
      "127.0.0.1\tlocalhost\n"
501 ebe8ef17 Michael Hanselmann
      "192.168.1.1 router\n")
502 9440aeab Michael Hanselmann
503 899d2a81 Michael Hanselmann
504 a8083063 Iustin Pop
class TestShellQuoting(unittest.TestCase):
505 a8083063 Iustin Pop
  """Test case for shell quoting functions"""
506 a8083063 Iustin Pop
507 a8083063 Iustin Pop
  def testShellQuote(self):
508 a8083063 Iustin Pop
    self.assertEqual(ShellQuote('abc'), "abc")
509 a8083063 Iustin Pop
    self.assertEqual(ShellQuote('ab"c'), "'ab\"c'")
510 a8083063 Iustin Pop
    self.assertEqual(ShellQuote("a'bc"), "'a'\\''bc'")
511 a8083063 Iustin Pop
    self.assertEqual(ShellQuote("a b c"), "'a b c'")
512 a8083063 Iustin Pop
    self.assertEqual(ShellQuote("a b\\ c"), "'a b\\ c'")
513 a8083063 Iustin Pop
514 a8083063 Iustin Pop
  def testShellQuoteArgs(self):
515 a8083063 Iustin Pop
    self.assertEqual(ShellQuoteArgs(['a', 'b', 'c']), "a b c")
516 a8083063 Iustin Pop
    self.assertEqual(ShellQuoteArgs(['a', 'b"', 'c']), "a 'b\"' c")
517 a8083063 Iustin Pop
    self.assertEqual(ShellQuoteArgs(['a', 'b\'', 'c']), "a 'b'\\\''' c")
518 a8083063 Iustin Pop
519 a8083063 Iustin Pop
520 2c30e9d7 Alexander Schreiber
class TestTcpPing(unittest.TestCase):
521 2c30e9d7 Alexander Schreiber
  """Testcase for TCP version of ping - against listen(2)ing port"""
522 2c30e9d7 Alexander Schreiber
523 2c30e9d7 Alexander Schreiber
  def setUp(self):
524 2c30e9d7 Alexander Schreiber
    self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
525 16abfbc2 Alexander Schreiber
    self.listener.bind((constants.LOCALHOST_IP_ADDRESS, 0))
526 2c30e9d7 Alexander Schreiber
    self.listenerport = self.listener.getsockname()[1]
527 2c30e9d7 Alexander Schreiber
    self.listener.listen(1)
528 2c30e9d7 Alexander Schreiber
529 2c30e9d7 Alexander Schreiber
  def tearDown(self):
530 2c30e9d7 Alexander Schreiber
    self.listener.shutdown(socket.SHUT_RDWR)
531 2c30e9d7 Alexander Schreiber
    del self.listener
532 2c30e9d7 Alexander Schreiber
    del self.listenerport
533 2c30e9d7 Alexander Schreiber
534 2c30e9d7 Alexander Schreiber
  def testTcpPingToLocalHostAccept(self):
535 16abfbc2 Alexander Schreiber
    self.assert_(TcpPing(constants.LOCALHOST_IP_ADDRESS,
536 16abfbc2 Alexander Schreiber
                         constants.LOCALHOST_IP_ADDRESS,
537 2c30e9d7 Alexander Schreiber
                         self.listenerport,
538 2c30e9d7 Alexander Schreiber
                         timeout=10,
539 2c30e9d7 Alexander Schreiber
                         live_port_needed=True),
540 2c30e9d7 Alexander Schreiber
                 "failed to connect to test listener")
541 2c30e9d7 Alexander Schreiber
542 2c30e9d7 Alexander Schreiber
543 2c30e9d7 Alexander Schreiber
class TestTcpPingDeaf(unittest.TestCase):
544 2c30e9d7 Alexander Schreiber
  """Testcase for TCP version of ping - against non listen(2)ing port"""
545 2c30e9d7 Alexander Schreiber
546 2c30e9d7 Alexander Schreiber
  def setUp(self):
547 2c30e9d7 Alexander Schreiber
    self.deaflistener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
548 16abfbc2 Alexander Schreiber
    self.deaflistener.bind((constants.LOCALHOST_IP_ADDRESS, 0))
549 2c30e9d7 Alexander Schreiber
    self.deaflistenerport = self.deaflistener.getsockname()[1]
550 2c30e9d7 Alexander Schreiber
551 2c30e9d7 Alexander Schreiber
  def tearDown(self):
552 2c30e9d7 Alexander Schreiber
    del self.deaflistener
553 2c30e9d7 Alexander Schreiber
    del self.deaflistenerport
554 2c30e9d7 Alexander Schreiber
555 2c30e9d7 Alexander Schreiber
  def testTcpPingToLocalHostAcceptDeaf(self):
556 16abfbc2 Alexander Schreiber
    self.failIf(TcpPing(constants.LOCALHOST_IP_ADDRESS,
557 16abfbc2 Alexander Schreiber
                        constants.LOCALHOST_IP_ADDRESS,
558 2c30e9d7 Alexander Schreiber
                        self.deaflistenerport,
559 16abfbc2 Alexander Schreiber
                        timeout=constants.TCP_PING_TIMEOUT,
560 2c30e9d7 Alexander Schreiber
                        live_port_needed=True), # need successful connect(2)
561 2c30e9d7 Alexander Schreiber
                "successfully connected to deaf listener")
562 2c30e9d7 Alexander Schreiber
563 2c30e9d7 Alexander Schreiber
  def testTcpPingToLocalHostNoAccept(self):
564 16abfbc2 Alexander Schreiber
    self.assert_(TcpPing(constants.LOCALHOST_IP_ADDRESS,
565 16abfbc2 Alexander Schreiber
                         constants.LOCALHOST_IP_ADDRESS,
566 2c30e9d7 Alexander Schreiber
                         self.deaflistenerport,
567 16abfbc2 Alexander Schreiber
                         timeout=constants.TCP_PING_TIMEOUT,
568 2c30e9d7 Alexander Schreiber
                         live_port_needed=False), # ECONNREFUSED is OK
569 2c30e9d7 Alexander Schreiber
                 "failed to ping alive host on deaf port")
570 2c30e9d7 Alexander Schreiber
571 2c30e9d7 Alexander Schreiber
572 eedbda4b Michael Hanselmann
class TestListVisibleFiles(unittest.TestCase):
573 eedbda4b Michael Hanselmann
  """Test case for ListVisibleFiles"""
574 eedbda4b Michael Hanselmann
575 eedbda4b Michael Hanselmann
  def setUp(self):
576 eedbda4b Michael Hanselmann
    self.path = tempfile.mkdtemp()
577 eedbda4b Michael Hanselmann
578 eedbda4b Michael Hanselmann
  def tearDown(self):
579 eedbda4b Michael Hanselmann
    shutil.rmtree(self.path)
580 eedbda4b Michael Hanselmann
581 eedbda4b Michael Hanselmann
  def _test(self, files, expected):
582 eedbda4b Michael Hanselmann
    # Sort a copy
583 eedbda4b Michael Hanselmann
    expected = expected[:]
584 eedbda4b Michael Hanselmann
    expected.sort()
585 eedbda4b Michael Hanselmann
586 eedbda4b Michael Hanselmann
    for name in files:
587 eedbda4b Michael Hanselmann
      f = open(os.path.join(self.path, name), 'w')
588 eedbda4b Michael Hanselmann
      try:
589 eedbda4b Michael Hanselmann
        f.write("Test\n")
590 eedbda4b Michael Hanselmann
      finally:
591 eedbda4b Michael Hanselmann
        f.close()
592 eedbda4b Michael Hanselmann
593 eedbda4b Michael Hanselmann
    found = ListVisibleFiles(self.path)
594 eedbda4b Michael Hanselmann
    found.sort()
595 eedbda4b Michael Hanselmann
596 eedbda4b Michael Hanselmann
    self.assertEqual(found, expected)
597 eedbda4b Michael Hanselmann
598 eedbda4b Michael Hanselmann
  def testAllVisible(self):
599 eedbda4b Michael Hanselmann
    files = ["a", "b", "c"]
600 eedbda4b Michael Hanselmann
    expected = files
601 eedbda4b Michael Hanselmann
    self._test(files, expected)
602 eedbda4b Michael Hanselmann
603 eedbda4b Michael Hanselmann
  def testNoneVisible(self):
604 eedbda4b Michael Hanselmann
    files = [".a", ".b", ".c"]
605 eedbda4b Michael Hanselmann
    expected = []
606 eedbda4b Michael Hanselmann
    self._test(files, expected)
607 eedbda4b Michael Hanselmann
608 eedbda4b Michael Hanselmann
  def testSomeVisible(self):
609 eedbda4b Michael Hanselmann
    files = ["a", "b", ".c"]
610 eedbda4b Michael Hanselmann
    expected = ["a", "b"]
611 eedbda4b Michael Hanselmann
    self._test(files, expected)
612 eedbda4b Michael Hanselmann
613 eedbda4b Michael Hanselmann
614 24818e8f Michael Hanselmann
class TestNewUUID(unittest.TestCase):
615 24818e8f Michael Hanselmann
  """Test case for NewUUID"""
616 59072e7e Michael Hanselmann
617 59072e7e Michael Hanselmann
  _re_uuid = re.compile('^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-'
618 59072e7e Michael Hanselmann
                        '[a-f0-9]{4}-[a-f0-9]{12}$')
619 59072e7e Michael Hanselmann
620 59072e7e Michael Hanselmann
  def runTest(self):
621 24818e8f Michael Hanselmann
    self.failUnless(self._re_uuid.match(utils.NewUUID()))
622 59072e7e Michael Hanselmann
623 59072e7e Michael Hanselmann
624 f7414041 Michael Hanselmann
class TestUniqueSequence(unittest.TestCase):
625 f7414041 Michael Hanselmann
  """Test case for UniqueSequence"""
626 f7414041 Michael Hanselmann
627 f7414041 Michael Hanselmann
  def _test(self, input, expected):
628 f7414041 Michael Hanselmann
    self.assertEqual(utils.UniqueSequence(input), expected)
629 f7414041 Michael Hanselmann
630 f7414041 Michael Hanselmann
  def runTest(self):
631 f7414041 Michael Hanselmann
    # Ordered input
632 f7414041 Michael Hanselmann
    self._test([1, 2, 3], [1, 2, 3])
633 f7414041 Michael Hanselmann
    self._test([1, 1, 2, 2, 3, 3], [1, 2, 3])
634 f7414041 Michael Hanselmann
    self._test([1, 2, 2, 3], [1, 2, 3])
635 f7414041 Michael Hanselmann
    self._test([1, 2, 3, 3], [1, 2, 3])
636 f7414041 Michael Hanselmann
637 f7414041 Michael Hanselmann
    # Unordered input
638 f7414041 Michael Hanselmann
    self._test([1, 2, 3, 1, 2, 3], [1, 2, 3])
639 f7414041 Michael Hanselmann
    self._test([1, 1, 2, 3, 3, 1, 2], [1, 2, 3])
640 f7414041 Michael Hanselmann
641 f7414041 Michael Hanselmann
    # Strings
642 f7414041 Michael Hanselmann
    self._test(["a", "a"], ["a"])
643 f7414041 Michael Hanselmann
    self._test(["a", "b"], ["a", "b"])
644 f7414041 Michael Hanselmann
    self._test(["a", "b", "a"], ["a", "b"])
645 f7414041 Michael Hanselmann
646 f7414041 Michael Hanselmann
647 a8083063 Iustin Pop
if __name__ == '__main__':
648 a8083063 Iustin Pop
  unittest.main()