Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.utils_unittest.py @ d6646186

History | View | Annotate | Download (20.1 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 16abfbc2 Alexander Schreiber
from ganeti import constants
37 59072e7e Michael Hanselmann
from ganeti import utils
38 a8083063 Iustin Pop
from ganeti.utils import IsProcessAlive, Lock, Unlock, RunCmd, \
39 a8083063 Iustin Pop
     RemoveFile, CheckDict, MatchNameComponent, FormatUnit, \
40 a8083063 Iustin Pop
     ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \
41 899d2a81 Michael Hanselmann
     ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
42 9440aeab Michael Hanselmann
     SetEtcHostsEntry, RemoveEtcHostsEntry
43 a8083063 Iustin Pop
from ganeti.errors import LockError, UnitParseError
44 a8083063 Iustin Pop
45 2c30e9d7 Alexander Schreiber
46 f0990e0c Michael Hanselmann
class GanetiTestCase(unittest.TestCase):
47 f0990e0c Michael Hanselmann
  def assertFileContent(self, file_name, content):
48 f0990e0c Michael Hanselmann
    """Checks the content of a file.
49 f0990e0c Michael Hanselmann

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