[Inteproxy-commits] r168 - in trunk: . inteproxy test
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Tue Nov 25 17:59:04 CET 2008
Author: bh
Date: 2008-11-25 17:59:04 +0100 (Tue, 25 Nov 2008)
New Revision: 168
Added:
trunk/inteproxy/config.py
trunk/test/filesupport.py
trunk/test/test_config.py
Modified:
trunk/ChangeLog
trunk/test/support.py
Log:
* inteproxy/config.py: New module to read the config file. The
config file now contains optional proxy settings
* test/test_config.py: New. Test cases for the config file
reading code
* test/filesupport.py: New. Support code for tests involving
files.
* test/support.py (AttributeTestMixin): New mixin to help test
attributes of an object.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2008-11-24 14:36:30 UTC (rev 167)
+++ trunk/ChangeLog 2008-11-25 16:59:04 UTC (rev 168)
@@ -1,3 +1,17 @@
+2008-11-25 Bernhard Herzog <bh at intevation.de>
+
+ * inteproxy/config.py: New module to read the config file. The
+ config file now contains optional proxy settings
+
+ * test/test_config.py: New. Test cases for the config file
+ reading code
+
+ * test/filesupport.py: New. Support code for tests involving
+ files.
+
+ * test/support.py (AttributeTestMixin): New mixin to help test
+ attributes of an object.
+
2008-09-30 Bernhard Herzog <bh at intevation.de>
* website/index-de.htm4, website/index.htm4: Updated Intevation's
Added: trunk/inteproxy/config.py
===================================================================
--- trunk/inteproxy/config.py 2008-11-24 14:36:30 UTC (rev 167)
+++ trunk/inteproxy/config.py 2008-11-25 16:59:04 UTC (rev 168)
@@ -0,0 +1,120 @@
+# Copyright (C) 2007, 2008 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh at intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""Read the InteProxy config file."""
+
+import sys
+from ConfigParser import SafeConfigParser, NoOptionError, NoSectionError
+
+
+# special object to indicate no default value
+nodefault = object()
+
+class Struct(object):
+
+ def __init__(self, **kw):
+ self.__dict__.update(**kw)
+
+
+class Option(object):
+
+ def __init__(self, name, default=nodefault, converter=lambda x: x,
+ attribute=None):
+ self.name = name
+ self.default = default
+ self.converter = converter
+ if not attribute:
+ attribute = name
+ self.attribute = attribute
+
+ def has_default(self):
+ return self.default is not nodefault
+
+
+class HostEntry(object):
+
+ def __init__(self, host, path, classname):
+ self.host = host
+ self.path = path
+ self.classname = classname
+
+ def __repr__(self):
+ return "Host(%r, %r, %r)" % (self.host, self.path, self.classname)
+
+ def __eq__(self, other):
+ return (self.host == other.host
+ and self.path == other.path
+ and self.classname == other.classname)
+
+ def __lt__(self, other):
+ return (self.host < other.host
+ or self.path < other.path
+ or self.classname < other.classname)
+
+
+inteproxy_desc = [Option("http_proxy", default=None),
+ Option("https_proxy", default=None)]
+
+proxy_desc = [Option("host"),
+ Option("port", default=80, converter=int),
+ Option("username", default=None),
+ Option("password", default=None)]
+
+host_desc = [Option("host"),
+ Option("path"),
+ Option("class", attribute="classname")]
+
+
+def read_config_section(parser, section, item_desc, defaults=None,
+ settings_class=Struct):
+ if defaults is None:
+ defaults = dict()
+ options = dict()
+ for item in item_desc:
+ try:
+ value = item.converter(parser.get(section, item.name,
+ vars=defaults))
+ except (NoOptionError, NoSectionError):
+ if item.has_default():
+ value = item.default
+ else:
+ print >>sys.stderr, "Missing option %r in section %r" \
+ % (item.name, section)
+ sys.exit(1)
+
+ options[item.attribute] = value
+
+ return settings_class(**options)
+
+
+def read_config(filename):
+ """Reads the InteProxy configuration from the file given by filename.
+ """
+ parser = SafeConfigParser()
+ parser.read([filename])
+
+ remote_hosts = []
+ config = None
+
+ sections = set(parser.sections())
+ config = read_config_section(parser, "inteproxy", inteproxy_desc)
+ sections.discard("inteproxy")
+
+ for proxy_attr in ("http_proxy", "https_proxy"):
+ section = getattr(config, proxy_attr)
+ if section is not None:
+ setattr(config, proxy_attr, read_config_section(parser, section,
+ proxy_desc))
+ sections.discard(section)
+
+ config.hosts = []
+ for section in sections:
+ config.hosts.append(read_config_section(parser, section, host_desc,
+ settings_class=HostEntry))
+
+
+ return config
Property changes on: trunk/inteproxy/config.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
Added: trunk/test/filesupport.py
===================================================================
--- trunk/test/filesupport.py 2008-11-24 14:36:30 UTC (rev 167)
+++ trunk/test/filesupport.py 2008-11-25 16:59:04 UTC (rev 168)
@@ -0,0 +1,145 @@
+# Copyright (C) 2007, 2008 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh at intevation.de>
+
+"""Support code for the test cases"""
+
+import os
+import tempfile
+import shutil
+
+
+def writefile(filename, contents, permissions=None):
+ """Write contents to filename in an atomic way.
+
+ The contents are first written to a temporary file in the same
+ directory as filename. Once the contents are written, the temporary
+ file is closed and renamed to filename.
+
+ The optional parameter permissions, if given, are the permissions
+ for the new file. By default, or if the parameter is None, the
+ default permissions set by the tempfile.mkstemp are used which means
+ that the file is only readable for the user that created the file.
+ The permissions value is used as the second parameter to os.chmod.
+ """
+ dirname, basename = os.path.split(filename)
+ fileno, tempname = tempfile.mkstemp("", basename, dirname)
+ try:
+ os.write(fileno, contents)
+ if not contents.endswith("\n"):
+ os.write(fileno, "\n")
+ os.close(fileno)
+ if permissions is not None:
+ os.chmod(tempname, permissions)
+ os.rename(tempname, filename)
+ finally:
+ if os.path.exists(tempname):
+ os.remove(tempname)
+
+
+def create_temp_dir():
+ """Create a temporary directory for the test-suite and return its name.
+
+ The temporary directory is always called temp and is created in the
+ directory where the support module is located.
+
+ If the temp directory already exists, just return the name.
+ """
+ name = os.path.abspath(os.path.join(os.path.dirname(__file__), "temp"))
+
+ # if the directory already exists, we're done
+ if os.path.isdir(name):
+ return name
+
+ # create the directory
+ os.mkdir(name)
+ return name
+
+
+class FileTestMixin:
+
+ """Mixin class for tests that use temporary files.
+
+ Instances of this class create a test-specific sub-directory under
+ the main test temp directory. The name of the sub-directory is the
+ return value of the test's id() method.
+ """
+
+ _test_specific_directory_created = False
+
+ def create_test_specific_temp_dir(self):
+ """Creates the test specific directory and returns its absolute name.
+ When this method is called for the first time and the directory
+ exists, if is removed, so that a specific test instance always
+ starts with an empty directory.
+ """
+ dirname = os.path.join(create_temp_dir(), self.id())
+ if not self._test_specific_directory_created:
+ if os.path.exists(dirname):
+ shutil.rmtree(dirname)
+ os.mkdir(dirname)
+ self._test_specific_directory_created = True
+ return dirname
+
+ def temp_file_name(self, basename):
+ """Returns the full name of the file named basename in the temp. dir.
+ """
+ return os.path.join(self.create_test_specific_temp_dir(), basename)
+
+ def create_temp_file(self, basename, contents, permissions=None):
+ """Creates a file in the temp directory with the given contents.
+ The optional parameter permissions should either be None (the
+ default) or an int specifying the file permissions (same format
+ as the second parameter of os.chmod). The method returns the
+ absolute name of the created file.
+ """
+ filename = self.temp_file_name(basename)
+ file = open(filename, "w")
+ file.write(contents)
+ file.close()
+ if permissions is not None:
+ os.chmod(filename, permissions)
+ return filename
+
+ def create_temp_dir(self, basename):
+ """Creates the directory basename in the temporary directory.
+ The method returns the absolute name of the created directory.
+ """
+ dirname = self.temp_file_name(basename)
+ os.mkdir(dirname)
+ return dirname
+
+ def create_files(self, directory, filedesc):
+ """Creates a hierarchy of directories and files in directory.
+ The filedesc parameter should be a sequence of (name, contents)
+ pairs or (name, permissions, contents) triples. Each item of
+ the sequence describes one entry of the directory. If contents
+ is an instance of list, the entry is a subdirectory and the
+ contents is a list in the same format as filedesc and passed
+ recursively to the create_files method. If contents is a
+ string, the new directory entry is a normal file and contents is
+ the contents of the file. The permissions if present, should be
+ an int specifying the files permissions (setting permissions
+ doesn't work yet for directories).
+
+ The method returns the absolute name of the toplevel directory
+ created by the method.
+ """
+ directory = self.temp_file_name(directory)
+ os.makedirs(directory)
+ for item in filedesc:
+ if len(item) == 3:
+ name, permissions, contents = item
+ else:
+ name, contents = item
+ permissions = None # use default permissions
+ if isinstance(contents, list):
+ # a list as contents indicates a directory
+ self.create_files(os.path.join(directory, name), contents)
+ else:
+ writefile(os.path.join(directory, name), contents, permissions)
+ return directory
+
+ def check_file_contents(self, filename, contents):
+ """check the contents of a file"""
+ self.assertEquals(open(filename).read(), contents)
Property changes on: trunk/test/filesupport.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
Modified: trunk/test/support.py
===================================================================
--- trunk/test/support.py 2008-11-24 14:36:30 UTC (rev 167)
+++ trunk/test/support.py 2008-11-25 16:59:04 UTC (rev 168)
@@ -1,4 +1,4 @@
-# Copyright (C) 2007 by Intevation GmbH
+# Copyright (C) 2007, 2008 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh at intevation.de>
#
@@ -46,3 +46,10 @@
if mode is not None:
os.chmod(filename, mode)
return filename
+
+
+class AttributeTestMixin:
+
+ def check_attributes(self, obj, **attrs):
+ for key, value in attrs.items():
+ self.assertEquals(getattr(obj, key), value)
Added: trunk/test/test_config.py
===================================================================
--- trunk/test/test_config.py 2008-11-24 14:36:30 UTC (rev 167)
+++ trunk/test/test_config.py 2008-11-25 16:59:04 UTC (rev 168)
@@ -0,0 +1,122 @@
+# Copyright (C) 2008 by Intevation GmbH
+# Authors:
+# Bernhard Herzog <bh at intevation.de>
+#
+# This program is free software under the GPL (>=v2)
+# Read the file COPYING coming with the software for details.
+
+"""Tests for the inteproxy.config module"""
+
+import sys
+import os
+import operator
+import unittest
+
+from filesupport import FileTestMixin
+from support import AttributeTestMixin
+
+from inteproxy.config import read_config, HostEntry
+
+
+class ReadConfigTest(unittest.TestCase, FileTestMixin, AttributeTestMixin):
+
+ def setUp(self):
+ self.config_file = self.create_temp_file("test.cfg",
+ self.config_contents)
+
+
+class TestReadConfigEmptyInteproxySection(ReadConfigTest):
+
+ config_contents = """\
+[inteproxy]
+"""
+
+ def test_readconfig(self):
+ self.check_attributes(read_config(self.config_file),
+ http_proxy=None,
+ https_proxy=None)
+
+
+class TestReadConfigNoInteproxySection(ReadConfigTest):
+
+ config_contents = """\
+"""
+
+ def test_readconfig(self):
+ self.check_attributes(read_config(self.config_file),
+ http_proxy=None,
+ https_proxy=None)
+
+
+class TestReadConfigHttpProxyNoPortNoAuth(ReadConfigTest):
+
+ config_contents = """\
+[inteproxy]
+http_proxy=local_proxy
+
+[local_proxy]
+host=localhost
+"""
+
+ def test_readconfig(self):
+ config = read_config(self.config_file)
+ self.check_attributes(config, https_proxy=None)
+ self.check_attributes(config.http_proxy,
+ host="localhost", port=80,
+ username=None, password=None)
+
+
+class TestReadConfigHttpsProxyWithPortAndAuth(ReadConfigTest):
+
+ config_contents = """\
+[inteproxy]
+https_proxy=https_proxy
+
+[https_proxy]
+host=localhost
+port=8080
+username=john
+password=secret
+"""
+
+ def test_readconfig(self):
+ config = read_config(self.config_file)
+ self.check_attributes(config, http_proxy=None)
+ self.check_attributes(config.https_proxy,
+ host="localhost", port=8080,
+ username="john", password="secret")
+
+class TestReadConfigProxyAndHosts(ReadConfigTest):
+
+ config_contents = """\
+[inteproxy]
+https_proxy=https_proxy
+
+[https_proxy]
+host=localhost
+port=8080
+username=john
+password=secret
+
+[inteproxy-demo.intevation.org]
+host=inteproxy-demo.intevation.org
+path=/cgi-bin/frida-wms
+class=owsproxy
+
+[wms.example.org]
+host=wms.example.org
+path=/wms
+class=basicauth
+"""
+
+ def test_readconfig(self):
+ config = read_config(self.config_file)
+ self.check_attributes(config, http_proxy=None)
+ self.check_attributes(config.https_proxy,
+ host="localhost", port=8080,
+ username="john", password="secret")
+ self.assertEquals(sorted(config.hosts),
+ [HostEntry("inteproxy-demo.intevation.org",
+ "/cgi-bin/frida-wms", "owsproxy"),
+ HostEntry("wms.example.org", "/wms", "basicauth"),
+ ])
Property changes on: trunk/test/test_config.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
More information about the Inteproxy-commits
mailing list