Skip to content

Commit

Permalink
updated CI configuration; added a lot of layering tests
Browse files Browse the repository at this point in the history
  • Loading branch information
staffanm committed Jan 24, 2016
1 parent fa12188 commit 191e05c
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 29 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ python:
- "2.6"

before_install:
- curl -L https://github.com/coreos/etcd/releases/download/v0.4.6/etcd-v0.4.6-linux-amd64.tar.gz -o etcd-v0.4.6-linux-amd64.tar.gz
- tar xzvf etcd-v0.4.6-linux-amd64.tar.gz
- cd etcd-v0.4.6-linux-amd64
- curl -L https://github.com/coreos/etcd/releases/download/v2.2.4/etcd-v2.2.4-linux-amd64.tar.gz -o etcd-v2.2.4-linux-amd64.tar.gz
- tar xzvf etcd-v2.2.4-linux-amd64.tar.gz
- cd etcd-v2.2.4-linux-amd64
- ./etcd > /dev/null &
- cd ..

install:
- pip install -r requirements.txt
- pip install coveralls
- pip install coverage==3.7.1 coveralls

# command to run tests, e.g. python setup.py test
script:
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ environment:
- PYTHON: "C:/Python27"
- PYTHON: "C:/Python34"
init:
- ps: Invoke-WebRequest "https://raw.github.com/pypa/pip/master/contrib/get-pip.py" -OutFile "c:/get-pip.py"
- ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py"
- ps: "git config --global core.autocrlf false" # always use unix lineendings
install:
- "%PYTHON%/python.exe c:/get-pip.py"
Expand Down
6 changes: 6 additions & 0 deletions layeredconfig/etcdstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ def set(self, key=None, value=None):
if key and value:
self.dirtyvalues[key] = value

def _strvalue(self, value):
if isinstance(value, bool):
return str(value).lower()
else:
return super(EtcdStore, self)._strvalue(value)

def save(self):
for k in self.dirtyvalues:
requests.put(self.source+self.sectionkey+k,
Expand Down
161 changes: 137 additions & 24 deletions tests/test_layeredconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ def _test_config_subsections(self, cfg):
date_want = "2014-10-15" # recommended date serialization
self.assertEqual(cfg.mymodule.expires, date_want)

def _test_layered_configs(self, cfg):
self.assertEqual(cfg.home, 'otherdata')
self.assertEqual(cfg.mymodule.force, False)


class TestConfigSourceHelper(TestLayeredConfigHelper):

Expand Down Expand Up @@ -189,6 +193,15 @@ def test_config_subsections(self):
cfg = LayeredConfig(self.complex)
self._test_config_subsections(cfg)

def test_layered_subsections(self):
# this testcases excercies a bug (or rather a deficency in the
# file-based sources) -- if the highest-priority source has
# subsections, and a lower-priority file-based source lacks
# those subsections, bad things would happen.
#
# see https://github.com/staffanm/layeredconfig/issues/2
cfg = LayeredConfig(self.complex, self.extra)
self._test_layered_configs(cfg)

# common helper
class TestINIFileHelper(object):
Expand Down Expand Up @@ -223,6 +236,13 @@ def setUp(self):
unique = True
""")

with open("extra.ini", "w") as fp:
fp.write("""
[__root__]
home = otherdata
""")


def tearDown(self):
super(TestINIFileHelper, self).tearDown()
os.unlink("simple.ini")
Expand Down Expand Up @@ -253,6 +273,7 @@ class TestDefaults(unittest.TestCase, TestConfigSourceHelper):
},
'extramodule': {'unique': True}})

extra = Defaults({'home': 'otherdata'})

class TestINIFile(TestINIFileHelper, unittest.TestCase,
TestConfigSourceHelper):
Expand All @@ -264,6 +285,7 @@ def setUp(self):
super(TestINIFile, self).setUp()
self.simple = INIFile("simple.ini")
self.complex = INIFile("complex.ini")
self.extra = INIFile("extra.ini")

# Overrides of TestHelper.test_get, .test_typed and
# .test_subsection_nested due to limitations of INIFile
Expand Down Expand Up @@ -403,12 +425,17 @@ def setUp(self):
"extramodule": {"unique": true}
}
""")
with open("extra.json", "w") as fp:
fp.write('{"home": "otherdata"}')

self.simple = JSONFile("simple.json")
self.complex = JSONFile("complex.json")
self.extra = JSONFile("extra.json")

def tearDown(self):
os.unlink("simple.json")
os.unlink("complex.json")
os.unlink("extra.json")

def test_get(self):
self.assertEqual(self.simple.get("home"), "mydata")
Expand Down Expand Up @@ -498,12 +525,19 @@ def setUp(self):
extramodule:
unique: true
""")
with open("extra.yaml", "w") as fp:
fp.write("""
home: otherdata
""")

self.simple = YAMLFile("simple.yaml")
self.complex = YAMLFile("complex.yaml")
self.extra = YAMLFile("extra.yaml")

def tearDown(self):
os.unlink("simple.yaml")
os.unlink("complex.yaml")
os.unlink("extra.yaml")

# Also, strings are unicode when they need to be,
# str otherwise.
Expand Down Expand Up @@ -620,12 +654,24 @@ def setUp(self):
</dict>
</plist>
""")
with open("extra.plist", "w") as fp:
fp.write("""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>home</key>
<string>otherdata</string>
</dict>
</plist>
""")
self.simple = PListFile("simple.plist")
self.complex = PListFile("complex.plist")
self.extra = PListFile("extra.plist")

def tearDown(self):
os.unlink("simple.plist")
os.unlink("complex.plist")
os.unlink("extra.plist")

# override only because plists cannot handle date objects (only datetime)
def test_get(self):
Expand Down Expand Up @@ -731,13 +777,21 @@ def setUp(self):
extramodule = Subsection()
extramodule.unique = True
""")

with open("extra.py", "w") as fp:
fp.write("""from __future__ import unicode_literals
home = 'otherdata'
""")
self.simple = PyFile("simple.py")
self.complex = PyFile("complex.py")
self.extra = PyFile("extra.py")

def tearDown(self):
os.unlink("simple.py")
os.unlink("complex.py")
os.unlink("extra.py")


class TestCommandline(unittest.TestCase, TestConfigSourceHelper):
Expand Down Expand Up @@ -799,6 +853,10 @@ def test_config_subsections(self):
self.supported_types = (str, list)
super(TestCommandline, self).test_config_subsections()

def test_layered_subsections(self):
pass # this test has no meaning for Command line arguments
# (can't have two sets of them)

def test_set(self):
self.simple.set("home", "away from home")
self.assertEqual(self.simple.get("home"), "away from home")
Expand Down Expand Up @@ -842,6 +900,10 @@ def test_config_subsections(self):
# re-enable the original impl of test_config_subsections
TestConfigSourceHelper.test_config_subsections(self)

def test_layered_subsections(self):
pass # this test has no meaning for Command line arguments
# (can't have two sets of them)

def test_typed(self):
# re-enable the original impl of test_get
TestConfigSourceHelper.test_typed(self)
Expand Down Expand Up @@ -877,6 +939,10 @@ def test_get(self):
self.assertEqual(self.simple.get("expires"), "2014-10-15")
self.assertEqual(self.simple.get("lastrun"), "2014-10-15 14:32:07")

def test_layered_subsections(self):
pass # this test has no meaning for environment variables
# (can't have two sets of them)

def test_typed(self):
for key in self.simple.keys():
self.assertFalse(self.simple.typed(key))
Expand Down Expand Up @@ -909,7 +975,7 @@ def simple(self):
self._clear_server()
requests.put(ETCD_BASE + "/home", data={'value': 'mydata'})
requests.put(ETCD_BASE + "/processes", data={'value': '4'})
requests.put(ETCD_BASE + "/force", data={'value': "True"})
requests.put(ETCD_BASE + "/force", data={'value': "true"})
requests.put(ETCD_BASE + "/extra", data={'value': "foo, bar"})
requests.put(ETCD_BASE + "/expires", data={'value': "2014-10-15"})
requests.put(ETCD_BASE + "/lastrun", data={'value': "2014-10-15 14:32:07"})
Expand All @@ -920,15 +986,22 @@ def complex(self):
self._clear_server()
requests.put(ETCD_BASE + "/home", data={'value': "mydata"})
requests.put(ETCD_BASE + "/processes", data={'value': "4"})
requests.put(ETCD_BASE + "/force", data={'value': "True"})
requests.put(ETCD_BASE + "/force", data={'value': "true"})
requests.put(ETCD_BASE + "/extra", data={'value': "foo, bar"})
requests.put(ETCD_BASE + "/mymodule/force", data={'value': "False"})
requests.put(ETCD_BASE + "/mymodule/force", data={'value': "false"})
requests.put(ETCD_BASE + "/mymodule/extra", data={'value': "foo, baz"})
requests.put(ETCD_BASE + "/mymodule/expires", data={'value': "2014-10-15"})
requests.put(ETCD_BASE + "/mymodule/arbitrary/nesting/depth", data={'value': "works"})
requests.put(ETCD_BASE + "/extramodule/unique", data={'value': "True"})
requests.put(ETCD_BASE + "/extramodule/unique", data={'value': "true"})
return EtcdStore()

def test_layered_subsections(self):
pass # this test has no meaning for etcd stores, as a single
# server cannot have two sets of configuration to layere
# (however, we could concievably have two distinct
# servers -- but that isn't supported on ground of being
# too complicated)

def test_typed(self):
for key in self.simple.keys():
self.assertFalse(self.simple.typed(key))
Expand Down Expand Up @@ -970,7 +1043,6 @@ def indexfilter(node):
want = """
{
"dir": true,
"key": "/",
"nodes": [
{
"createdIndex": 4627,
Expand Down Expand Up @@ -1244,25 +1316,6 @@ def test_layered_subsections(self):
self.assertEqual(['force', 'home', 'loglevel'], list(cfg.mymodule))


def test_layered_yaml(self):
# see https://github.com/staffanm/layeredconfig/issues/2
with open("1.yaml", "w") as fp:
fp.write("""a:
b: b
""")
with open("2.yaml", "w") as fp:
fp.write("somevar: value")

try:
yamls = [YAMLFile('1.yaml'), YAMLFile('2.yaml')]
cfg = LayeredConfig(*yamls)
self.assertEqual(cfg.somevar, 'value')
self.assertEqual(cfg.a.b, 'b')
finally:
os.unlink("1.yaml")
os.unlink("2.yaml")


class TestSubsections(unittest.TestCase):
def test_list(self):
defaults = {'home': 'mydata',
Expand All @@ -1273,6 +1326,66 @@ def test_list(self):
set(cfg.subsection))


class TestLayeredSubsections(unittest.TestCase):

def _test_subsection(self, primary, secondary, cls):
with open("primary.txt", "w") as fp:
fp.write(primary)
with open("secondary.txt", "w") as fp:
fp.write(secondary)
try:
srcs = [cls('primary.txt'), cls('secondary.txt')]
cfg = LayeredConfig(*srcs)
self.assertEqual(cfg.somevar, 'value')
self.assertEqual(cfg.a.b, 'b')
finally:
os.unlink("primary.txt")
os.unlink("secondary.txt")

def test_layered_yaml(self):
self._test_subsection("""a:
b: b
""", "somevar: value", YAMLFile)

def test_layered_ini(self):
self._test_subsection("""
[__root__]
[a]
b = b
""", """
[__root__]
somevar = value
""", INIFile)

def test_layered_json(self):
self._test_subsection('{"a": {"b": "b"} }',
'{"somevar": "value"}',
JSONFile)

def test_layered_plist(self):
self._test_subsection("""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>a</key>
<dict>
<key>b</key>
<string>b</string>
</dict>
</dict>
</plist>""", """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>somevar</key>
<string>value</string>
</dict>
</plist>""", PListFile)



class TestModifications(TestINIFileHelper, unittest.TestCase):
def test_modified(self):
defaults = {'lastdownload': None}
Expand Down

0 comments on commit 191e05c

Please sign in to comment.