Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.utils_unittest.py @ d4fa5c23

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