فهرست منبع

- logfile_name() now accepts mixed case for reserved words, like boolean().
- dict_of_key_value_pairs() now strips all values, not just the last one.
- Added some unit tests for other datatypes functions.

Mike Naberezny 17 سال پیش
والد
کامیت
31bc1c150e
2فایلهای تغییر یافته به همراه158 افزوده شده و 16 حذف شده
  1. 25 12
      src/supervisor/datatypes.py
  2. 133 4
      src/supervisor/tests/test_datatypes.py

+ 25 - 12
src/supervisor/datatypes.py

@@ -38,12 +38,15 @@ def integer(value):
     except OverflowError:
         return long(value)
 
+TRUTHY_STRINGS = ('yes', 'true', 'on', '1')
+FALSY_STRINGS  = ('no', 'false', 'off', '0')
+
 def boolean(s):
     """Convert a string value to a boolean value."""
     ss = str(s).lower()
-    if ss in ('yes', 'true', 'on', '1'):
+    if ss in TRUTHY_STRINGS:
         return True
-    elif ss in ('no', 'false', 'off', '0'):
+    elif ss in FALSY_STRINGS:
         return False
     else:
         raise ValueError("not a valid boolean value: " + repr(s))
@@ -74,23 +77,33 @@ def list_of_exitcodes(arg):
 def dict_of_key_value_pairs(arg):
     """ parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'} """
     D = {}
-    pairs = [ x.strip() for x in arg.split(',') ]
-    pairs = filter(None, pairs)
-    for pair in pairs:
-        try:
-            k, v = pair.split('=', 1)
-        except ValueError:
-            raise ValueError('Unknown key/value pair %s' % pair)
-        D[k] = v
+    try:
+        pairs = filter(None, arg.split(','))
+        for pair in pairs:
+            try:
+                k, v = pair.split('=', 1)
+            except ValueError:
+                raise ValueError('Unknown key/value pair %s' % pair)
+            D[k.strip()] = v.strip()
+    except:
+        raise ValueError("not a list of key/value pairs: " + repr(arg))        
     return D
 
 class Automatic:
     pass
 
+LOGFILE_NONES = ('none', 'off', None)
+LOGFILE_AUTOS = (Automatic, 'auto')
+
 def logfile_name(val):
-    if val in ('NONE', 'OFF', None):
+    if hasattr(val, 'lower'):
+        coerced = val.lower()
+    else:
+        coerced = val
+
+    if coerced in LOGFILE_NONES:
         return None
-    elif val in (Automatic, 'AUTO'):
+    elif coerced in LOGFILE_AUTOS:
         return Automatic
     else:
         return existing_dirpath(val)

+ 133 - 4
src/supervisor/tests/test_datatypes.py

@@ -5,13 +5,142 @@ import os
 import unittest
 import socket
 import tempfile
+from supervisor import datatypes
 
-from supervisor.datatypes import UnixStreamSocketConfig
-from supervisor.datatypes import InetStreamSocketConfig
+class DatatypesTest(unittest.TestCase):
+    def test_boolean_returns_true_for_truthy_values(self):
+        for s in datatypes.TRUTHY_STRINGS:    
+            actual = datatypes.boolean(s)
+            self.assertEqual(actual, True)
+
+    def test_boolean_returns_true_for_upper_truthy_values(self):
+        for s in map(str.upper, datatypes.TRUTHY_STRINGS):
+            actual = datatypes.boolean(s)
+            self.assert_(actual, True)
+
+    def test_boolean_returns_false_for_falsy_values(self):
+        for s in datatypes.FALSY_STRINGS:
+            actual = datatypes.boolean(s)
+            self.assertEqual(actual, False)
+
+    def test_boolean_returns_false_for_upper_falsy_values(self):
+        for s in map(str.upper, datatypes.FALSY_STRINGS):
+            actual = datatypes.boolean(s)
+            self.assertEqual(actual, False)
+
+    def test_boolean_raises_value_error_for_bad_value(self):
+        self.assertRaises(ValueError, 
+                          datatypes.boolean, 'not-a-value')
+
+    def test_list_of_strings_returns_empty_list_for_empty_string(self):
+        actual = datatypes.list_of_strings('')
+        self.assertEqual(actual, [])
+
+    def test_list_of_strings_returns_list_of_strings_by_comma_split(self):
+        actual = datatypes.list_of_strings('foo,bar')
+        self.assertEqual(actual, ['foo', 'bar'])
+
+    def test_list_of_strings_returns_list_of_strings_by_comma_split(self):
+        actual = datatypes.list_of_strings('foo,bar')
+        self.assertEqual(actual, ['foo', 'bar'])
+
+    def test_list_of_strings_returns_strings_with_whitespace_stripped(self):
+        actual = datatypes.list_of_strings(' foo , bar ')
+        self.assertEqual(actual, ['foo', 'bar'])
+
+    def test_list_of_strings_raises_value_error_when_comma_split_fails(self):
+        self.assertRaises(ValueError,
+                          datatypes.list_of_strings, 42)
+
+    def test_list_of_ints_returns_empty_list_for_empty_string(self):
+        actual = datatypes.list_of_ints('')
+        self.assertEqual(actual, [])
+    
+    def test_list_of_ints_returns_list_of_ints_by_comma_split(self):
+        actual = datatypes.list_of_ints('1,42')
+        self.assertEqual(actual, [1,42])
+        
+    def test_list_of_ints_returns_ints_even_if_whitespace_in_string(self):
+        actual = datatypes.list_of_ints(' 1 , 42 ')
+        self.assertEqual(actual, [1,42])
+        
+    def test_list_of_ints_raises_value_error_when_comma_split_fails(self):
+        self.assertRaises(ValueError,
+                          datatypes.list_of_ints, 42)
+
+    def test_list_of_ints_raises_value_error_when_one_value_is_bad(self):
+        self.assertRaises(ValueError,
+                          datatypes.list_of_ints, '1, bad, 42')
+
+    def test_list_of_exitcodes_just_delegates_to_list_of_ints(self):
+        func = datatypes.list_of_ints
+        datatypes.list_of_ints = lambda s: 'test-spy'
+        try:
+            actual = datatypes.list_of_exitcodes('1,2,3')
+            self.assertEqual(actual, 'test-spy')
+        finally:
+            datatypes.list_of_ints = func
+    
+    def test_hasattr_automatic(self):
+        datatypes.Automatic
+
+    def test_dict_of_key_value_pairs_returns_empty_dict_for_empty_str(self):
+        actual = datatypes.dict_of_key_value_pairs('')
+        self.assertEqual({}, actual)
+    
+    def test_dict_of_key_value_pairs_returns_dict_from_single_pair_str(self):
+        actual = datatypes.dict_of_key_value_pairs('foo=bar')
+        expected = {'foo': 'bar'}
+        self.assertEqual(actual, expected)
+
+    def test_dict_of_key_value_pairs_returns_dict_from_multi_pair_str(self):
+        actual = datatypes.dict_of_key_value_pairs('foo=bar,baz=qux')
+        expected = {'foo': 'bar', 'baz': 'qux'}
+        self.assertEqual(actual, expected)
+
+    def test_dict_of_key_value_pairs_returns_dict_even_if_whitespace(self):
+        actual = datatypes.dict_of_key_value_pairs(' foo = bar , baz = qux ')
+        expected = {'foo': 'bar', 'baz': 'qux'}
+        self.assertEqual(actual, expected)
+
+    def test_logfile_name_returns_none_for_none_values(self):
+        for thing in datatypes.LOGFILE_NONES:
+            actual = datatypes.logfile_name(thing)
+            self.assertEqual(actual, None)        
+    
+    def test_logfile_name_returns_none_for_uppered_none_values(self):
+        for thing in datatypes.LOGFILE_NONES:
+            if hasattr(thing, 'upper'):
+                thing = thing.upper()
+            actual = datatypes.logfile_name(thing)
+            self.assertEqual(actual, None)
+
+    def test_logfile_name_returns_automatic_for_auto_values(self):
+        for thing in datatypes.LOGFILE_AUTOS:
+            actual = datatypes.logfile_name(thing)
+            self.assertEqual(actual, datatypes.Automatic)        
+
+    def test_logfile_name_returns_automatic_for_uppered_auto_values(self):
+        for thing in datatypes.LOGFILE_AUTOS:
+            if hasattr(thing, 'upper'):
+                thing = thing.upper()
+            actual = datatypes.logfile_name(thing)
+            self.assertEqual(actual, datatypes.Automatic)        
+
+    def test_logfile_name_returns_existing_dirpath_for_other_values(self):
+        func = datatypes.existing_dirpath
+        datatypes.existing_dirpath = lambda path: path
+        try:
+            path = '/path/to/logfile/With/Case/Preserved'
+            actual = datatypes.logfile_name(path)
+            self.assertEqual(actual, path)
+        finally:
+            datatypes.existing_dirpath = func
+    
 
 class InetStreamSocketConfigTests(unittest.TestCase):
     def _getTargetClass(self):
-        return InetStreamSocketConfig
+        return datatypes.InetStreamSocketConfig
         
     def _makeOne(self, *args, **kw):
         return self._getTargetClass()(*args, **kw)
@@ -46,7 +175,7 @@ class InetStreamSocketConfigTests(unittest.TestCase):
         
 class UnixStreamSocketConfigTests(unittest.TestCase):
     def _getTargetClass(self):
-        return UnixStreamSocketConfig
+        return datatypes.UnixStreamSocketConfig
         
     def _makeOne(self, *args, **kw):
         return self._getTargetClass()(*args, **kw)