diff --git a/.travis.yml b/.travis.yml index 7951a6671c..72794f4c19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,19 +2,16 @@ sudo: required dist: trusty language: java jdk: - - oraclejdk8 -#before_install: -# - cat /etc/hosts # optionally check the content *before* -# - sudo hostname "$(hostname | cut -c1-63)" -# - sed -e "s/^\\(127\\.0\\.0\\.1.*\\)/\\1 $(hostname | cut -c1-63)/" /etc/hosts | sudo tee /etc/hosts -# - sudo mv /tmp/hosts /etc/hosts -# - cat /etc/hosts # optionally check the content *after* + - oraclejdk8 +install: +- sudo apt-get install ant-optional addons: hosts: - myshorthost hostname: myshorthost -script: ant -buildfile ./framework/build.xml test +script: +- ant -buildfile ./framework/build.xml test after_failure: find samples-and-tests -name '*.failed.html' -exec echo {} \; -exec cat {} \; diff --git a/documentation/manual/production.textile b/documentation/manual/production.textile index 9876b0063f..8101005e2b 100644 --- a/documentation/manual/production.textile +++ b/documentation/manual/production.textile @@ -189,7 +189,8 @@ bc. # X509 certificates certificate.key.file=conf/host.key certificate.file=conf/host.cert # In case your key file is password protected -certificate.password=secret +# certificate.key.file=conf/host.pass.key +# certificate.password=secret trustmanager.algorithm=JKS If you are using keystore: @@ -202,8 +203,13 @@ Note that the values above are the default values. You can generate self-signed certificates using *openssl*: -bc. openssl genrsa 1024 > host.key -openssl req -new -x509 -nodes -sha1 -days 365 -key host.key > host.cert +bc. openssl genrsa -des3 -passout pass:secret -out host.pass.key 2048 +openssl rsa -passin pass:secret -in host.pass.key -out host.key +openssl req -new -key host.key -out host.csr -subj '/C=GB/ST=Test State or Province/L=Test Locality/O=Organization Name/OU=Organizational Unit Name/CN=Common Name/emailAddress=test@email.address' +openssl x509 -req -days 3650 -in host.csr -signkey host.key -out host.cert + +note. the first command creates a password-protected-key ('host.pass.key'). +the second command converts/writes the same key ('host.key') without password protection. If you are using the Java keystore mechanism, then the following properties can be configured in your @application.conf@ file: diff --git a/framework/build.xml b/framework/build.xml index 0a5750d240..b768d24d68 100644 --- a/framework/build.xml +++ b/framework/build.xml @@ -1,6 +1,6 @@ - + @@ -254,6 +254,12 @@ + + + + + + @@ -261,11 +267,11 @@ - + - + @@ -273,6 +279,8 @@ + + @@ -322,9 +330,11 @@ - - - + + + + + diff --git a/framework/dependencies.yml b/framework/dependencies.yml index 39227487a5..75f9f0364e 100644 --- a/framework/dependencies.yml +++ b/framework/dependencies.yml @@ -42,7 +42,8 @@ require: &allDependencies - oauth.signpost -> signpost-core 1.2.1.2 - org.apache.geronimo.specs -> geronimo-servlet_2.5_spec 1.2 - org.apache.ivy -> ivy 2.4.0 - - org.bouncycastle -> bcprov-jdk15 1.46 + - org.bouncycastle -> bcprov-jdk15on 1.57 + - org.bouncycastle -> bcpkix-jdk15on 1.57 - org.codehaus.groovy -> groovy-all 2.4.11 - org.eclipse.jdt.core 3.12.3 - org.hibernate -> hibernate-core 5.2.10.patched diff --git a/framework/lib/bcpkix-jdk15on-1.57.jar b/framework/lib/bcpkix-jdk15on-1.57.jar new file mode 100644 index 0000000000..5ce7d5c5cc Binary files /dev/null and b/framework/lib/bcpkix-jdk15on-1.57.jar differ diff --git a/framework/lib/bcprov-jdk15-1.46.jar b/framework/lib/bcprov-jdk15-1.46.jar deleted file mode 100644 index daa0b54cc0..0000000000 Binary files a/framework/lib/bcprov-jdk15-1.46.jar and /dev/null differ diff --git a/framework/lib/bcprov-jdk15on-1.57.jar b/framework/lib/bcprov-jdk15on-1.57.jar new file mode 100644 index 0000000000..5a10986b3a Binary files /dev/null and b/framework/lib/bcprov-jdk15on-1.57.jar differ diff --git a/framework/pym/play/commands/autotest.py b/framework/pym/play/commands/autotest.py index b1b0ef17ee..3b082bd8fa 100644 --- a/framework/pym/play/commands/autotest.py +++ b/framework/pym/play/commands/autotest.py @@ -118,15 +118,17 @@ def autotest(app, args): if app.readConf('headlessBrowser'): headless_browser = app.readConf('headlessBrowser') - fpcp = [os.path.join(app.play_env["basedir"], 'modules/testrunner/lib/play-testrunner.jar')] + fpcp = [] + fpcp.append(os.path.normpath(os.path.join(app.play_env["basedir"], 'modules/testrunner/conf'))) + fpcp.append(os.path.join(app.play_env["basedir"], 'modules/testrunner/lib/play-testrunner.jar')) fpcp_libs = os.path.join(app.play_env["basedir"], 'modules/testrunner/firephoque') for jar in os.listdir(fpcp_libs): if jar.endswith('.jar'): fpcp.append(os.path.normpath(os.path.join(fpcp_libs, jar))) cp_args = ':'.join(fpcp) if os.name == 'nt': - cp_args = ';'.join(fpcp) - java_cmd = [java_path()] + add_options + ['-classpath', cp_args, '-Dapplication.url=%s://localhost:%s' % (protocol, http_port), '-DheadlessBrowser=%s' % (headless_browser), 'play.modules.testrunner.FirePhoque'] + cp_args = ';'.join(fpcp) + java_cmd = [java_path()] + add_options + ['-Djava.util.logging.config.file=logging.properties', '-classpath', cp_args, '-Dapplication.url=%s://localhost:%s' % (protocol, http_port), '-DheadlessBrowser=%s' % (headless_browser), 'play.modules.testrunner.FirePhoque'] if protocol == 'https': java_cmd.insert(-1, '-Djavax.net.ssl.trustStore=' + app.readConf('keystore.file')) try: diff --git a/framework/src/play/Logger.java b/framework/src/play/Logger.java index 5d957896d0..01d6bcdfff 100644 --- a/framework/src/play/Logger.java +++ b/framework/src/play/Logger.java @@ -2,7 +2,10 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Paths; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; @@ -70,10 +73,11 @@ public static void init() { PropertyConfigurator.configure(shutUp); } else if (Logger.log4j == null) { - if (log4jConf.getFile().indexOf(Play.applicationPath.getAbsolutePath()) == 0) { - // The log4j configuration file is located somewhere in the application folder, - // so it's probably a custom configuration file - configuredManually = true; + try { + if (Paths.get(log4jConf.toURI()).startsWith(Play.applicationPath.toPath())) { + configuredManually = true; + } + } catch (IllegalArgumentException | FileSystemNotFoundException | SecurityException | URISyntaxException e) { } if (isXMLConfig) { DOMConfigurator.configure(log4jConf); diff --git a/framework/src/play/server/ssl/SslHttpServerContextFactory.java b/framework/src/play/server/ssl/SslHttpServerContextFactory.java index 7f0642462f..e123209861 100644 --- a/framework/src/play/server/ssl/SslHttpServerContextFactory.java +++ b/framework/src/play/server/ssl/SslHttpServerContextFactory.java @@ -1,19 +1,24 @@ package play.server.ssl; +import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMReader; -import org.bouncycastle.openssl.PasswordFinder; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; import play.Logger; import play.Play; import javax.net.ssl.*; +import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.net.Socket; import java.security.*; import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.List; +import java.util.Collection; import java.util.Properties; public class SslHttpServerContextFactory { @@ -84,18 +89,22 @@ public PEMKeyManager() { final Properties p = Play.configuration; String keyFile = p.getProperty("certificate.key.file", "conf/host.key"); - try (PEMReader keyReader = new PEMReader(new FileReader(Play.getFile(keyFile)), new PEMPasswordFinder())) { - key = ((KeyPair) keyReader.readObject()).getPrivate(); - - try (PEMReader reader = new PEMReader(new FileReader(Play.getFile(p.getProperty("certificate.file", "conf/host.cert"))))) { - X509Certificate cert; - List chainVector = new ArrayList<>(); - - while ((cert = (X509Certificate) reader.readObject()) != null) { - chainVector.add(cert); - } - chain = chainVector.toArray(new X509Certificate[1]); + try (PEMParser keyReader = new PEMParser(new FileReader(Play.getFile(keyFile)))) { + final Object object = keyReader.readObject(); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + final KeyPair keyPair; + if (object instanceof PEMEncryptedKeyPair) { + PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder() + .build(Play.configuration.getProperty("certificate.password", "secret").toCharArray()); + keyPair = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)); + } else { + keyPair = converter.getKeyPair((PEMKeyPair) object); } + key = keyPair.getPrivate(); + + final File hostCertFile = Play.getFile(p.getProperty("certificate.file", "conf/host.cert")); + final Collection collection = new CertificateFactory().engineGenerateCertificates(new FileInputStream(hostCertFile)); + chain = (X509Certificate[]) collection.toArray(new X509Certificate[collection.size()]); } catch (Exception e) { Logger.error(e, "Failed to initialize PEMKeyManager from file %s", keyFile); } @@ -136,12 +145,4 @@ public PrivateKey getPrivateKey(String s) { return key; } } - - private static class PEMPasswordFinder implements PasswordFinder { - @Override - public char[] getPassword() { - return Play.configuration.getProperty("certificate.password", "secret").toCharArray(); - } - } - } diff --git a/framework/test-src/play/templates/FastTagsTest.java b/framework/test-src/play/templates/FastTagsTest.java index a09fba715f..edf57c38ce 100644 --- a/framework/test-src/play/templates/FastTagsTest.java +++ b/framework/test-src/play/templates/FastTagsTest.java @@ -1,6 +1,7 @@ package play.templates; import groovy.lang.Closure; +import org.junit.After; import org.junit.Before; import org.junit.Test; import play.mvc.Http; @@ -18,15 +19,27 @@ public class FastTagsTest { private StringWriter out = new StringWriter(); + final String backupSystemLineBreak = System.getProperty("line.separator"); @Before public void setUp() throws Exception { + //if you render html into out + // and expect results with line breaks + // take into account that your tests will fail on other platforms + // force line.separator be the same on any platform + // or use String.format in expected code with the placeholder '%n' for any expected line separation. + System.setProperty("line.separator","\n"); Http.Response.current.set(new Http.Response()); Http.Response.current().encoding = "UTF-8"; Scope.Session.current.set(new Scope.Session()); Scope.Session.current().put("___AT", "1234"); } + @After + public void tearDown() throws Exception { + // restore line.separator + System.setProperty("line.separator", backupSystemLineBreak); + } @Test public void _form_simple() throws Exception { diff --git a/modules/testrunner/conf/logging.properties b/modules/testrunner/conf/logging.properties new file mode 100644 index 0000000000..0f72c98279 --- /dev/null +++ b/modules/testrunner/conf/logging.properties @@ -0,0 +1,61 @@ +############################################################ +# Default Logging Configuration File +# +# You can use a different file by specifying a filename +# with the java.util.logging.config.file system property. +# For example java -Djava.util.logging.config.file=myfile +############################################################ + +############################################################ +# Global properties +############################################################ + +# "handlers" specifies a comma separated list of log Handler +# classes. These handlers will be installed during VM startup. +# Note that these classes must be on the system classpath. +# By default we only configure a ConsoleHandler, which will only +# show messages at the INFO and above levels. +handlers= java.util.logging.ConsoleHandler + +# To also add the FileHandler, use the following line instead. +#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler + +# Default global logging level. +# This specifies which kinds of events are logged across +# all loggers. For any given facility this global level +# can be overriden by a facility specific level +# Note that the ConsoleHandler also has a separate level +# setting to limit messages printed to the console. +.level= INFO + +############################################################ +# Handler specific properties. +# Describes specific configuration info for Handlers. +############################################################ + +# default file output is in user's home directory. +java.util.logging.FileHandler.pattern = %h/java%u.log +java.util.logging.FileHandler.limit = 50000 +java.util.logging.FileHandler.count = 1 +java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter + +# Limit the message that are printed on the console to INFO and above. +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + +# Example to customize the SimpleFormatter output format +# to print one-line log message like this: +# : [] +# +# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n + +############################################################ +# Facility specific properties. +# Provides extra control for each logger. +############################################################ + +# For example, set the com.xyz.foo logger to only log SEVERE +# messages: +com.gargoylesoftware.htmlunit.DefaultCssErrorHandler = OFF +com.gargoylesoftware.htmlunit = OFF +org.apache.http.impl.execchain.RetryExec = OFF \ No newline at end of file diff --git a/modules/testrunner/src/play/modules/testrunner/FirePhoque.java b/modules/testrunner/src/play/modules/testrunner/FirePhoque.java index 8b5bc5f308..9d61b93456 100644 --- a/modules/testrunner/src/play/modules/testrunner/FirePhoque.java +++ b/modules/testrunner/src/play/modules/testrunner/FirePhoque.java @@ -32,9 +32,6 @@ public class FirePhoque { public static void main(String[] args) throws Exception { - Logger.getLogger(DefaultCssErrorHandler.class.getName()).setLevel(Level.OFF); - Logger.getLogger("com.gargoylesoftware.htmlunit").setLevel(Level.OFF); - String app = System.getProperty("application.url", "http://localhost:9000"); // Tests description diff --git a/python/Lib/unittest.py b/python/Lib/unittest.py new file mode 100644 index 0000000000..09c6ca97c8 --- /dev/null +++ b/python/Lib/unittest.py @@ -0,0 +1,872 @@ +#!/usr/bin/env python +''' +Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's +Smalltalk testing framework. + +This module contains the core framework classes that form the basis of +specific test cases and suites (TestCase, TestSuite etc.), and also a +text-based utility class for running the tests and reporting the results + (TextTestRunner). + +Simple usage: + + import unittest + + class IntegerArithmenticTestCase(unittest.TestCase): + def testAdd(self): ## test method names begin 'test*' + self.assertEquals((1 + 2), 3) + self.assertEquals(0 + 1, 1) + def testMultiply(self): + self.assertEquals((0 * 10), 0) + self.assertEquals((5 * 8), 40) + + if __name__ == '__main__': + unittest.main() + +Further information is available in the bundled documentation, and from + + http://docs.python.org/lib/module-unittest.html + +Copyright (c) 1999-2003 Steve Purcell +This module is free software, and you may redistribute it and/or modify +it under the same terms as Python itself, so long as this copyright message +and disclaimer are retained in their original form. + +IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +''' + +__author__ = "Steve Purcell" +__email__ = "stephen_purcell at yahoo dot com" +__version__ = "#Revision: 1.63 $"[11:-2] + +import time +import sys +import traceback +import os +import types + +############################################################################## +# Exported classes and functions +############################################################################## +__all__ = ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner', + 'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader'] + +# Expose obsolete functions for backwards compatibility +__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases']) + + +############################################################################## +# Backward compatibility +############################################################################## +if sys.version_info[:2] < (2, 2): + def isinstance(obj, clsinfo): + import __builtin__ + if type(clsinfo) in (tuple, list): + for cls in clsinfo: + if cls is type: cls = types.ClassType + if __builtin__.isinstance(obj, cls): + return 1 + return 0 + else: return __builtin__.isinstance(obj, clsinfo) + +def _CmpToKey(mycmp): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) == -1 + return K + +############################################################################## +# Test framework core +############################################################################## + +# All classes defined herein are 'new-style' classes, allowing use of 'super()' +__metaclass__ = type + +def _strclass(cls): + return "%s.%s" % (cls.__module__, cls.__name__) + +__unittest = 1 + +class TestResult: + """Holder for test result information. + + Test results are automatically managed by the TestCase and TestSuite + classes, and do not need to be explicitly manipulated by writers of tests. + + Each instance holds the total number of tests run, and collections of + failures and errors that occurred among those test runs. The collections + contain tuples of (testcase, exceptioninfo), where exceptioninfo is the + formatted traceback of the error that occurred. + """ + def __init__(self): + self.failures = [] + self.errors = [] + self.testsRun = 0 + self.shouldStop = False + + def startTest(self, test): + "Called when the given test is about to be run" + self.testsRun = self.testsRun + 1 + + def stopTest(self, test): + "Called when the given test has been run" + pass + + def addError(self, test, err): + """Called when an error has occurred. 'err' is a tuple of values as + returned by sys.exc_info(). + """ + self.errors.append((test, self._exc_info_to_string(err, test))) + + def addFailure(self, test, err): + """Called when an error has occurred. 'err' is a tuple of values as + returned by sys.exc_info().""" + self.failures.append((test, self._exc_info_to_string(err, test))) + + def addSuccess(self, test): + "Called when a test has completed successfully" + pass + + def wasSuccessful(self): + "Tells whether or not this result was a success" + return len(self.failures) == len(self.errors) == 0 + + def stop(self): + "Indicates that the tests should be aborted" + self.shouldStop = True + + def _exc_info_to_string(self, err, test): + """Converts a sys.exc_info()-style tuple of values into a string.""" + exctype, value, tb = err + # Skip test runner traceback levels + while tb and self._is_relevant_tb_level(tb): + tb = tb.tb_next + if exctype is test.failureException: + # Skip assert*() traceback levels + length = self._count_relevant_tb_levels(tb) + return ''.join(traceback.format_exception(exctype, value, tb, length)) + return ''.join(traceback.format_exception(exctype, value, tb)) + + def _is_relevant_tb_level(self, tb): + return '__unittest' in tb.tb_frame.f_globals + + def _count_relevant_tb_levels(self, tb): + length = 0 + while tb and not self._is_relevant_tb_level(tb): + length += 1 + tb = tb.tb_next + return length + + def __repr__(self): + return "<%s run=%i errors=%i failures=%i>" % \ + (_strclass(self.__class__), self.testsRun, len(self.errors), + len(self.failures)) + +class TestCase: + """A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + """ + + # This attribute determines which exception will be raised when + # the instance's assertion methods fail; test methods raising this + # exception will be deemed to have 'failed' rather than 'errored' + + failureException = AssertionError + + def __init__(self, methodName='runTest'): + """Create an instance of the class that will use the named test + method when executed. Raises a ValueError if the instance does + not have a method with the specified name. + """ + try: + self._testMethodName = methodName + testMethod = getattr(self, methodName) + self._testMethodDoc = testMethod.__doc__ + except AttributeError: + raise ValueError, "no such test method in %s: %s" % \ + (self.__class__, methodName) + + def setUp(self): + "Hook method for setting up the test fixture before exercising it." + pass + + def tearDown(self): + "Hook method for deconstructing the test fixture after testing it." + pass + + def countTestCases(self): + return 1 + + def defaultTestResult(self): + return TestResult() + + def shortDescription(self): + """Returns a one-line description of the test, or None if no + description has been provided. + + The default implementation of this method returns the first line of + the specified test method's docstring. + """ + doc = self._testMethodDoc + return doc and doc.split("\n")[0].strip() or None + + def id(self): + return "%s.%s" % (_strclass(self.__class__), self._testMethodName) + + def __eq__(self, other): + if type(self) is not type(other): + return False + + return self._testMethodName == other._testMethodName + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((type(self), self._testMethodName)) + + def __str__(self): + return "%s (%s)" % (self._testMethodName, _strclass(self.__class__)) + + def __repr__(self): + return "<%s testMethod=%s>" % \ + (_strclass(self.__class__), self._testMethodName) + + def run(self, result=None): + if result is None: result = self.defaultTestResult() + result.startTest(self) + testMethod = getattr(self, self._testMethodName) + try: + try: + self.setUp() + except KeyboardInterrupt: + raise + except: + result.addError(self, self._exc_info()) + return + + ok = False + try: + testMethod() + ok = True + except self.failureException: + result.addFailure(self, self._exc_info()) + except KeyboardInterrupt: + raise + except: + result.addError(self, self._exc_info()) + + try: + self.tearDown() + except KeyboardInterrupt: + raise + except: + result.addError(self, self._exc_info()) + ok = False + if ok: result.addSuccess(self) + finally: + result.stopTest(self) + + def __call__(self, *args, **kwds): + return self.run(*args, **kwds) + + def debug(self): + """Run the test without collecting errors in a TestResult""" + self.setUp() + getattr(self, self._testMethodName)() + self.tearDown() + + def _exc_info(self): + """Return a version of sys.exc_info() with the traceback frame + minimised; usually the top level of the traceback frame is not + needed. + """ + return sys.exc_info() + + def fail(self, msg=None): + """Fail immediately, with the given message.""" + raise self.failureException, msg + + def failIf(self, expr, msg=None): + "Fail the test if the expression is true." + if expr: raise self.failureException, msg + + def failUnless(self, expr, msg=None): + """Fail the test unless the expression is true.""" + if not expr: raise self.failureException, msg + + def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): + """Fail unless an exception of class excClass is thrown + by callableObj when invoked with arguments args and keyword + arguments kwargs. If a different type of exception is + thrown, it will not be caught, and the test case will be + deemed to have suffered an error, exactly as for an + unexpected exception. + """ + try: + callableObj(*args, **kwargs) + except excClass: + return + else: + if hasattr(excClass,'__name__'): excName = excClass.__name__ + else: excName = str(excClass) + raise self.failureException, "%s not raised" % excName + + def failUnlessEqual(self, first, second, msg=None): + """Fail if the two objects are unequal as determined by the '==' + operator. + """ + if not first == second: + raise self.failureException, \ + (msg or '%r != %r' % (first, second)) + + def failIfEqual(self, first, second, msg=None): + """Fail if the two objects are equal as determined by the '==' + operator. + """ + if first == second: + raise self.failureException, \ + (msg or '%r == %r' % (first, second)) + + def failUnlessAlmostEqual(self, first, second, places=7, msg=None): + """Fail if the two objects are unequal as determined by their + difference rounded to the given number of decimal places + (default 7) and comparing to zero. + + Note that decimal places (from zero) are usually not the same + as significant digits (measured from the most signficant digit). + """ + if round(abs(second-first), places) != 0: + raise self.failureException, \ + (msg or '%r != %r within %r places' % (first, second, places)) + + def failIfAlmostEqual(self, first, second, places=7, msg=None): + """Fail if the two objects are equal as determined by their + difference rounded to the given number of decimal places + (default 7) and comparing to zero. + + Note that decimal places (from zero) are usually not the same + as significant digits (measured from the most signficant digit). + """ + if round(abs(second-first), places) == 0: + raise self.failureException, \ + (msg or '%r == %r within %r places' % (first, second, places)) + + # Synonyms for assertion methods + + assertEqual = assertEquals = failUnlessEqual + + assertNotEqual = assertNotEquals = failIfEqual + + assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual + + assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual + + assertRaises = failUnlessRaises + + assert_ = assertTrue = failUnless + + assertFalse = failIf + + + +class TestSuite: + """A test suite is a composite test consisting of a number of TestCases. + + For use, create an instance of TestSuite, then add test case instances. + When all tests have been added, the suite can be passed to a test + runner, such as TextTestRunner. It will run the individual test cases + in the order in which they were added, aggregating the results. When + subclassing, do not forget to call the base class constructor. + """ + def __init__(self, tests=()): + self._tests = [] + self.addTests(tests) + + def __repr__(self): + return "<%s tests=%s>" % (_strclass(self.__class__), self._tests) + + __str__ = __repr__ + + def __eq__(self, other): + if type(self) is not type(other): + return False + return self._tests == other._tests + + def __ne__(self, other): + return not self == other + + # Can't guarantee hash invariant, so flag as unhashable + __hash__ = None + + def __iter__(self): + return iter(self._tests) + + def countTestCases(self): + cases = 0 + for test in self._tests: + cases += test.countTestCases() + return cases + + def addTest(self, test): + # sanity checks + if not hasattr(test, '__call__'): + raise TypeError("the test to add must be callable") + if (isinstance(test, (type, types.ClassType)) and + issubclass(test, (TestCase, TestSuite))): + raise TypeError("TestCases and TestSuites must be instantiated " + "before passing them to addTest()") + self._tests.append(test) + + def addTests(self, tests): + if isinstance(tests, basestring): + raise TypeError("tests must be an iterable of tests, not a string") + for test in tests: + self.addTest(test) + + def run(self, result): + for test in self._tests: + if result.shouldStop: + break + test(result) + return result + + def __call__(self, *args, **kwds): + return self.run(*args, **kwds) + + def debug(self): + """Run the tests without collecting errors in a TestResult""" + for test in self._tests: test.debug() + + +class FunctionTestCase(TestCase): + """A test case that wraps a test function. + + This is useful for slipping pre-existing test functions into the + unittest framework. Optionally, set-up and tidy-up functions can be + supplied. As with TestCase, the tidy-up ('tearDown') function will + always be called if the set-up ('setUp') function ran successfully. + """ + + def __init__(self, testFunc, setUp=None, tearDown=None, + description=None): + TestCase.__init__(self) + self.__setUpFunc = setUp + self.__tearDownFunc = tearDown + self.__testFunc = testFunc + self.__description = description + + def setUp(self): + if self.__setUpFunc is not None: + self.__setUpFunc() + + def tearDown(self): + if self.__tearDownFunc is not None: + self.__tearDownFunc() + + def runTest(self): + self.__testFunc() + + def id(self): + return self.__testFunc.__name__ + + def __eq__(self, other): + if type(self) is not type(other): + return False + + return self.__setUpFunc == other.__setUpFunc and \ + self.__tearDownFunc == other.__tearDownFunc and \ + self.__testFunc == other.__testFunc and \ + self.__description == other.__description + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((type(self), self.__setUpFunc, self.__tearDownFunc, + self.__testFunc, self.__description)) + + def __str__(self): + return "%s (%s)" % (_strclass(self.__class__), self.__testFunc.__name__) + + def __repr__(self): + return "<%s testFunc=%s>" % (_strclass(self.__class__), self.__testFunc) + + def shortDescription(self): + if self.__description is not None: return self.__description + doc = self.__testFunc.__doc__ + return doc and doc.split("\n")[0].strip() or None + + + +############################################################################## +# Locating and loading tests +############################################################################## + +class TestLoader: + """This class is responsible for loading tests according to various + criteria and returning them wrapped in a TestSuite + """ + testMethodPrefix = 'test' + sortTestMethodsUsing = cmp + suiteClass = TestSuite + + def loadTestsFromTestCase(self, testCaseClass): + """Return a suite of all tests cases contained in testCaseClass""" + if issubclass(testCaseClass, TestSuite): + raise TypeError("Test cases should not be derived from TestSuite. Maybe you meant to derive from TestCase?") + testCaseNames = self.getTestCaseNames(testCaseClass) + if not testCaseNames and hasattr(testCaseClass, 'runTest'): + testCaseNames = ['runTest'] + return self.suiteClass(map(testCaseClass, testCaseNames)) + + def loadTestsFromModule(self, module): + """Return a suite of all tests cases contained in the given module""" + tests = [] + for name in dir(module): + obj = getattr(module, name) + if (isinstance(obj, (type, types.ClassType)) and + issubclass(obj, TestCase)): + tests.append(self.loadTestsFromTestCase(obj)) + return self.suiteClass(tests) + + def loadTestsFromName(self, name, module=None): + """Return a suite of all tests cases given a string specifier. + + The name may resolve either to a module, a test case class, a + test method within a test case class, or a callable object which + returns a TestCase or TestSuite instance. + + The method optionally resolves the names relative to a given module. + """ + parts = name.split('.') + if module is None: + parts_copy = parts[:] + while parts_copy: + try: + module = __import__('.'.join(parts_copy)) + break + except ImportError: + del parts_copy[-1] + if not parts_copy: raise + parts = parts[1:] + obj = module + for part in parts: + parent, obj = obj, getattr(obj, part) + + if type(obj) == types.ModuleType: + return self.loadTestsFromModule(obj) + elif (isinstance(obj, (type, types.ClassType)) and + issubclass(obj, TestCase)): + return self.loadTestsFromTestCase(obj) + elif (type(obj) == types.UnboundMethodType and + isinstance(parent, (type, types.ClassType)) and + issubclass(parent, TestCase)): + return TestSuite([parent(obj.__name__)]) + elif isinstance(obj, TestSuite): + return obj + elif hasattr(obj, '__call__'): + test = obj() + if isinstance(test, TestSuite): + return test + elif isinstance(test, TestCase): + return TestSuite([test]) + else: + raise TypeError("calling %s returned %s, not a test" % + (obj, test)) + else: + raise TypeError("don't know how to make test from: %s" % obj) + + def loadTestsFromNames(self, names, module=None): + """Return a suite of all tests cases found using the given sequence + of string specifiers. See 'loadTestsFromName()'. + """ + suites = [self.loadTestsFromName(name, module) for name in names] + return self.suiteClass(suites) + + def getTestCaseNames(self, testCaseClass): + """Return a sorted sequence of method names found within testCaseClass + """ + def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix): + return attrname.startswith(prefix) and hasattr(getattr(testCaseClass, attrname), '__call__') + testFnNames = filter(isTestMethod, dir(testCaseClass)) + if self.sortTestMethodsUsing: + testFnNames.sort(key=_CmpToKey(self.sortTestMethodsUsing)) + return testFnNames + + + +defaultTestLoader = TestLoader() + + +############################################################################## +# Patches for old functions: these functions should be considered obsolete +############################################################################## + +def _makeLoader(prefix, sortUsing, suiteClass=None): + loader = TestLoader() + loader.sortTestMethodsUsing = sortUsing + loader.testMethodPrefix = prefix + if suiteClass: loader.suiteClass = suiteClass + return loader + +def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp): + return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass) + +def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite): + return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass) + +def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite): + return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module) + + +############################################################################## +# Text UI +############################################################################## + +class _WritelnDecorator: + """Used to decorate file-like objects with a handy 'writeln' method""" + def __init__(self,stream): + self.stream = stream + + def __getattr__(self, attr): + return getattr(self.stream,attr) + + def writeln(self, arg=None): + if arg: self.write(arg) + self.write('\n') # text-mode streams translate to \r\n if needed + + +class _TextTestResult(TestResult): + """A test result class that can print formatted text results to a stream. + + Used by TextTestRunner. + """ + separator1 = '=' * 70 + separator2 = '-' * 70 + + def __init__(self, stream, descriptions, verbosity): + TestResult.__init__(self) + self.stream = stream + self.showAll = verbosity > 1 + self.dots = verbosity == 1 + self.descriptions = descriptions + + def getDescription(self, test): + if self.descriptions: + return test.shortDescription() or str(test) + else: + return str(test) + + def startTest(self, test): + TestResult.startTest(self, test) + if self.showAll: + self.stream.write(self.getDescription(test)) + self.stream.write(" ... ") + self.stream.flush() + + def addSuccess(self, test): + TestResult.addSuccess(self, test) + if self.showAll: + self.stream.writeln("ok") + elif self.dots: + self.stream.write('.') + self.stream.flush() + + def addError(self, test, err): + TestResult.addError(self, test, err) + if self.showAll: + self.stream.writeln("ERROR") + elif self.dots: + self.stream.write('E') + self.stream.flush() + + def addFailure(self, test, err): + TestResult.addFailure(self, test, err) + if self.showAll: + self.stream.writeln("FAIL") + elif self.dots: + self.stream.write('F') + self.stream.flush() + + def printErrors(self): + if self.dots or self.showAll: + self.stream.writeln() + self.printErrorList('ERROR', self.errors) + self.printErrorList('FAIL', self.failures) + + def printErrorList(self, flavour, errors): + for test, err in errors: + self.stream.writeln(self.separator1) + self.stream.writeln("%s: %s" % (flavour,self.getDescription(test))) + self.stream.writeln(self.separator2) + self.stream.writeln("%s" % err) + + +class TextTestRunner: + """A test runner class that displays results in textual form. + + It prints out the names of tests as they are run, errors as they + occur, and a summary of the results at the end of the test run. + """ + def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1): + self.stream = _WritelnDecorator(stream) + self.descriptions = descriptions + self.verbosity = verbosity + + def _makeResult(self): + return _TextTestResult(self.stream, self.descriptions, self.verbosity) + + def run(self, test): + "Run the given test case or test suite." + result = self._makeResult() + startTime = time.time() + test(result) + stopTime = time.time() + timeTaken = stopTime - startTime + result.printErrors() + self.stream.writeln(result.separator2) + run = result.testsRun + self.stream.writeln("Ran %d test%s in %.3fs" % + (run, run != 1 and "s" or "", timeTaken)) + self.stream.writeln() + if not result.wasSuccessful(): + self.stream.write("FAILED (") + failed, errored = map(len, (result.failures, result.errors)) + if failed: + self.stream.write("failures=%d" % failed) + if errored: + if failed: self.stream.write(", ") + self.stream.write("errors=%d" % errored) + self.stream.writeln(")") + else: + self.stream.writeln("OK") + return result + + + +############################################################################## +# Facilities for running tests from the command line +############################################################################## + +class TestProgram: + """A command-line program that runs a set of tests; this is primarily + for making test modules conveniently executable. + """ + USAGE = """\ +Usage: %(progName)s [options] [test] [...] + +Options: + -h, --help Show this message + -v, --verbose Verbose output + -q, --quiet Minimal output + +Examples: + %(progName)s - run default set of tests + %(progName)s MyTestSuite - run suite 'MyTestSuite' + %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething + %(progName)s MyTestCase - run all 'test*' test methods + in MyTestCase +""" + def __init__(self, module='__main__', defaultTest=None, + argv=None, testRunner=TextTestRunner, + testLoader=defaultTestLoader): + if type(module) == type(''): + self.module = __import__(module) + for part in module.split('.')[1:]: + self.module = getattr(self.module, part) + else: + self.module = module + if argv is None: + argv = sys.argv + self.verbosity = 1 + self.defaultTest = defaultTest + self.testRunner = testRunner + self.testLoader = testLoader + self.progName = os.path.basename(argv[0]) + self.parseArgs(argv) + self.runTests() + + def usageExit(self, msg=None): + if msg: print msg + print self.USAGE % self.__dict__ + sys.exit(2) + + def parseArgs(self, argv): + import getopt + try: + options, args = getopt.getopt(argv[1:], 'hHvq', + ['help','verbose','quiet']) + for opt, value in options: + if opt in ('-h','-H','--help'): + self.usageExit() + if opt in ('-q','--quiet'): + self.verbosity = 0 + if opt in ('-v','--verbose'): + self.verbosity = 2 + if len(args) == 0 and self.defaultTest is None: + self.test = self.testLoader.loadTestsFromModule(self.module) + return + if len(args) > 0: + self.testNames = args + else: + self.testNames = (self.defaultTest,) + self.createTests() + except getopt.error, msg: + self.usageExit(msg) + + def createTests(self): + self.test = self.testLoader.loadTestsFromNames(self.testNames, + self.module) + + def runTests(self): + if isinstance(self.testRunner, (type, types.ClassType)): + try: + testRunner = self.testRunner(verbosity=self.verbosity) + except TypeError: + # didn't accept the verbosity argument + testRunner = self.testRunner() + else: + # it is assumed to be a TestRunner instance + testRunner = self.testRunner + result = testRunner.run(self.test) + sys.exit(not result.wasSuccessful()) + +main = TestProgram + + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/resources/application-skel/conf/application.conf b/resources/application-skel/conf/application.conf index 636be241fc..f2264fa261 100644 --- a/resources/application-skel/conf/application.conf +++ b/resources/application-skel/conf/application.conf @@ -231,6 +231,16 @@ mail.smtp=mock # For SSL, use the play.ssl.netty.pipeline property # play.ssl.netty.pipeline = play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.codec.http.HttpContentCompressor,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.ssl.SslPlayHandler +# # X509 certificates +# # the following values are default values +# certificate.key.file=conf/host.key +# # certificate.password used only if certificate.key.file is password protected +# certificate.password=secret +# certificate.file=conf/host.cert +# trustmanager.algorithm=JKS +# keystore.algorithm=JKS +# keystore.password=secret +# keystore.file=conf/certificate.jks # Open file from errors pages # ~~~~~ diff --git a/samples-and-tests/i-am-a-developer/tests.py b/samples-and-tests/i-am-a-developer/tests.py index cdca8268b8..b70784b48e 100755 --- a/samples-and-tests/i-am-a-developer/tests.py +++ b/samples-and-tests/i-am-a-developer/tests.py @@ -1,20 +1,179 @@ #!/usr/bin/python -import unittest import os import shutil -import sys +import ssl import subprocess -import re +import sys +import threading import time +import unittest import urllib2 + import mechanize -import threading + # --- TESTS class IamADeveloper(unittest.TestCase): - + + def testSSLConfig(self): + + # Testing ssl config + step('Hello, I am testing SSL config') + + self.working_directory = bootstrapWorkingDirectory('i-am-testing-ssl-config-here') + + # play new job-app + step('Create a new project') + + self.play = callPlay(self, ['new', '%s/sslconfigapp' % self.working_directory, '--name=SSLCONFIGAPP']) + self.assert_(waitFor(self.play, 'The new application will be created')) + self.assert_(waitFor(self.play, 'OK, the application is created')) + self.assert_(waitFor(self.play, 'Have fun!')) + + self.play.wait() + + app = '%s/sslconfigapp' % self.working_directory + + step('Add config and files') + insert(app, "app/controllers/Application.java", 13, ' Logger.info("I am ssl secured!");') + + edit(app, "conf/application.conf", 32, 'http.port=-1') + edit(app, "conf/application.conf", 33, 'https.port=9000') + edit(app, "conf/application.conf", 232, + 'play.ssl.netty.pipeline = play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.codec.http.HttpContentCompressor,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.ssl.SslPlayHandler') + create(app, 'conf/host.key') + insert(app, "conf/host.key", 1, '-----BEGIN RSA PRIVATE KEY-----') + insert(app, "conf/host.key", 2, 'MIIEpQIBAAKCAQEAoOx9pCR7rZ50S9FotKVD2+aC36Hj4TkXZTZwEnh/fWyuiH2O') + insert(app, "conf/host.key", 3, 'Paj/dTw60Jvll4jshlnRHfJ6yfc/o7YlDUanLrQJm7I3/t3YNgqYg3WXeUTl+GrN') + insert(app, "conf/host.key", 4, 'Hn/3QgFGYqKobu8kfrwP4IapQRqlq4ZSdlR/bWpxnYSCZoXeeoimoSUcLlqD5dw7') + insert(app, "conf/host.key", 5, '7v2BlG2gqL5+lr5Fx4mDC12vczoUMRg88+VuA1ezU4cuXDe2MbpJMd7rqGN0xK4b') + insert(app, "conf/host.key", 6, 'CwkFtSJqBM1TH/Czr1S52hKrDTTys9PVw+eZSKO7BCk+PDq5jjx337XOWiO0kSHf') + insert(app, "conf/host.key", 7, 'V64x68xTojfzTzF304byr2Ytq6DjNbpZKwdYBwIDAQABAoIBAQCc6z7w6mp3uIWq') + insert(app, "conf/host.key", 8, '0P6K+ISdT7/aliCCJIu9tEHAoSOgiHQAwH4NflfsV9j6RqqxA2Gw+LBDxYkanDDA') + insert(app, "conf/host.key", 9, 'UQL8WSL5FbIw0q5rpqQIvnhN6ELWi+q8PFjcHuhawqeB0x7vXd52fqf0xxsQUw2t') + insert(app, "conf/host.key", 10, 'noOWw3qmlR9I/Eez9WImlk314RwDzc/bUsfBQhMKbNVHxstR8Q9YQQMp+xb9dqbL') + insert(app, "conf/host.key", 11, '3lfz3O70Q/Xc/JxXIOkqcfyoIT9CvpJf2MT1tkd1xolAV+4UJQwKQURlMKqcp7Yi') + insert(app, "conf/host.key", 12, 'NIxqv27ZGuhdzPCSFy3zcCIYMxXVvU+oSncGMlBpyf8ONDH2wZ7/nOtaz4Kf9tNZ') + insert(app, "conf/host.key", 13, 'ZcqtXd1RAoGBAM7DFMBd78hkJhLztXO5PqB3O87f438aDlQfIGDzi9/KD+Jy1TRz') + insert(app, "conf/host.key", 14, 'tJMLjmhPIOuy477k6+P3MmF3KeIjFzZg2Je56++rdpdX+E09Ts4s1gZkUAAfEyeI') + insert(app, "conf/host.key", 15, 'QJ53lrXJu0ShmXODSyEc+rtaUgsM0geL7EtacmrUQQI9yKbrUHmHw0glAoGBAMc+') + insert(app, "conf/host.key", 16, '9D13ne8bFLQ7upY6GuidgvG+ilAKaJ1YWNWjolTIV86RCEYNmgqxF0BzGT2Db55L') + insert(app, "conf/host.key", 17, 'Myt5epDOKJr0RRi7ddidUJFOAOfm/yciPbr+D34LCnj6rkdauAhYsjfjuWDNLHyf') + insert(app, "conf/host.key", 18, 'hjpBvvtMfqWE79vfIwVCKOy9xUVjqfZY2KDBu4G7AoGBAMSmjooXzgOOHRhRavdR') + insert(app, "conf/host.key", 19, '7Nq6DMxJ7RnqMk6X/De57ANBL7J0/YsRsWFZ0GwtNmZ2kl3xZNpBNk21BMTsExvJ') + insert(app, "conf/host.key", 20, 'KLfGQTyGnBh9ts/fy6AUzMrvhZdX9uPWl38gxtrHr7Eq8cQHz+ECqwaedQHFg81h') + insert(app, "conf/host.key", 21, 'q7BPqhspHVuAX+NCVBwCoB1xAoGBAME20mC9G6GgUE6LUWCXDjsfa7kEPlpqDZLv') + insert(app, "conf/host.key", 22, '9o2ONkAjW8sMJ8rPK99MZjDwrLxTNi153TA+iFXeJdBGKq9WMmyR+Ww/CW/ZOPt5') + insert(app, "conf/host.key", 23, 'IAWyk9F14Xz6E4FMfwRRBtpd8gnmTUq449CgqxRE1Ner93Hvi6VwyADz8lZc1Jf5') + insert(app, "conf/host.key", 24, 'BnG2DSA7AoGAAWRtgCEkhR/9GyLyAqoUd45FQdRdwIiDwRUsuazSMF2g+FSIfXqR') + insert(app, "conf/host.key", 25, 'MgEidXuKYTIRgsiDmgy6fy3XkSzaR1ehjC1uUyyiUzEd+guG9tURrRygR8S6VGw3') + insert(app, "conf/host.key", 26, 'mxX+1gneJnzA2cBminkc28ohIQegHEqKKif5gRsc2md+LsvDNR93io4=') + insert(app, "conf/host.key", 27, '-----END RSA PRIVATE KEY-----') + create(app, 'conf/host.pass.key') + insert(app, "conf/host.pass.key", 1, '-----BEGIN RSA PRIVATE KEY-----') + insert(app, "conf/host.pass.key", 2, 'Proc-Type: 4,ENCRYPTED') + insert(app, "conf/host.pass.key", 3, 'DEK-Info: DES-EDE3-CBC,FC6F4AA83014298F') + insert(app, "conf/host.pass.key", 4, '') + insert(app, "conf/host.pass.key", 5, 'ZxpC4NYQsMYCOfpMg3iRbQ5UQDBp50NGnT+wBgHnhTqXVUsIZ0x4eFvFKmIoGFne') + insert(app, "conf/host.pass.key", 6, 'hX2pnIMFpOJs4tRIItFyvjcwAARRZxg9KCkjL8cPBhNL4LNExYOTKE8QfTzTb9/l') + insert(app, "conf/host.pass.key", 7, 'DoF5EJraNwvXKlVNh9wrROW7oMJFqhkVRQN+lMnczTGPznnjbBvOr69ypU8/NWX/') + insert(app, "conf/host.pass.key", 8, 'JFgLYqBUnOPUKCaqxEuNzP632jOkhSdXmtl4ft1JFx/uoJG4rCGw5zOVHnTsCMbs') + insert(app, "conf/host.pass.key", 9, 'aWfzfYgnreKvSmwk+5J/0aHR14sXoJpPOk1KvJ3U347cJ/RB1hnnShAdEmYxqPmc') + insert(app, "conf/host.pass.key", 10, '7Hp2BXt86qlFs9SEBwptPtGmF+YAW7HdcgU0M1ONJ0/GysT4RWFJr5VO4QQWpQT/') + insert(app, "conf/host.pass.key", 11, 'DrX8odwKVSQHekmsJz4hD0CXj2v8KU7crbEtTemj3koxnbEn7gcZoGtTMmz37hZS') + insert(app, "conf/host.pass.key", 12, 'qJOolpPqHFV7WtheZ/+5ztSJ91eUgRqKTt1gLgQ6wbaCFfgsPIIRAjuklWnAyKxM') + insert(app, "conf/host.pass.key", 13, '0dxRb7pTCDLewZ7V2g9MzkF46r+eTCIw31NJC6EUsOYaj46bYbmdK5Smjqgc1z5S') + insert(app, "conf/host.pass.key", 14, 'jQGSFUUA+MRlLhx0e/old3fK1oUY1kujcDZcz57arykFDxNHSseFIauJOUeiw0Tp') + insert(app, "conf/host.pass.key", 15, '5nZJYtg4yWTEbLMi+iegu/pYZSbuy8APojIgPupg0FiFOED23J2ziXQs8ZxaG7w6') + insert(app, "conf/host.pass.key", 16, 'oc6SxWrubxCGt0dlEHAQnAB5eVZGcKCH4hVaF4w85j/oWf0Tw/kFAD1MqyiBPes3') + insert(app, "conf/host.pass.key", 17, 'BcrDyO4AJWpmocMZ5ERVkPhx1rqyRrpaYBMdTJ2LoQaKIGeDucfW3Iap0mk+jT31') + insert(app, "conf/host.pass.key", 18, 'RTVYNlCqoU1+oACqpV4mRQGW0BDIENvazCb+VJ0qHkedrM/Bx0Gxnx7jrlptOYEn') + insert(app, "conf/host.pass.key", 19, '2rU53bOIdwGw9+MjDV+jLKnxuwh56SI5wJzSBCr38jLlA/SgPDM+8K9AjeCJg0w5') + insert(app, "conf/host.pass.key", 20, 'C4Na4pDa3tSRwV2WsDJcLnWN+L1NoFNNMnePGzZHCBWaFI9WM2sZI5LsM+gZt37k') + insert(app, "conf/host.pass.key", 21, 'EnR/r8rn5Vig7hwxntW7D6IAka2Tkfl0Y+uvl373EGIv9d61/x6cxomPbYGwH0Sn') + insert(app, "conf/host.pass.key", 22, '6Emz3so5pXUuP8w2Gx7FNI9m7r+xOAfe87Eplc5DZiwtWyeSLOKDOnkwTxNdFMhk') + insert(app, "conf/host.pass.key", 23, 'GerNKG4RrMB5GEU0oI1rkMPlK4vf/K9ynHqLq5HjH839EzWH7aeqlo8059WMZ0Jz') + insert(app, "conf/host.pass.key", 24, 'qecDXcEZ2K9RkUPqGC2wdAGTyea/ElEWmplAWfqVHkD497IShQfTgJ23oLxFTDhd') + insert(app, "conf/host.pass.key", 25, 'IUso3Xj50N1U2+4JbYABv9zaXLRK+qTEPkTmeQHo+CJC0iIVQwGtQS9p3IcuLzKd') + insert(app, "conf/host.pass.key", 26, 's3wqL1Durxe+YVfHNqTYh2uC6eclSwA/21uDa59B37oK9Aymdzujps7IJQ147QWN') + insert(app, "conf/host.pass.key", 27, '4e39vDDrfPMthKiQAWm4f3+vduLxzShDgzLyVPDaYVfPAlD7UETz0x6eNCTZXDjg') + insert(app, "conf/host.pass.key", 28, 'S4JMnjhH8EFrzKdnUH40oeWa9RKKo5RwvRRRGNgR23OzcibI+54kl5DsMTI229+G') + insert(app, "conf/host.pass.key", 29, 'PDd5V4m+ahdfaPsM9DMr1mWGSN/hoLDJtMFPOiZP5R6OSTi99Tj5KJiglSdjmb6u') + insert(app, "conf/host.pass.key", 30, '-----END RSA PRIVATE KEY-----') + create(app, 'conf/host.cert') + insert(app, "conf/host.cert", 1, '-----BEGIN CERTIFICATE-----') + insert(app, "conf/host.cert", 2, 'MIID4DCCAsgCCQCdj5qAy7MGoTANBgkqhkiG9w0BAQsFADCBsTEfMB0GA1UECAwW') + insert(app, "conf/host.cert", 3, 'VGVzdCBTdGF0ZSBvciBQcm92aW5jZTEWMBQGA1UEBwwNVGVzdCBMb2NhbGl0eTEa') + insert(app, "conf/host.cert", 4, 'MBgGA1UECgwRT3JnYW5pemF0aW9uIE5hbWUxITAfBgNVBAsMGE9yZ2FuaXphdGlv') + insert(app, "conf/host.cert", 5, 'bmFsIFVuaXQgTmFtZTEUMBIGA1UEAwwLQ29tbW9uIE5hbWUxITAfBgkqhkiG9w0B') + insert(app, "conf/host.cert", 6, 'CQEWEnRlc3RAZW1haWwuYWRkcmVzczAeFw0xNzA1MjkxMjUyMDVaFw0yNzA1Mjcx') + insert(app, "conf/host.cert", 7, 'MjUyMDVaMIGxMR8wHQYDVQQIDBZUZXN0IFN0YXRlIG9yIFByb3ZpbmNlMRYwFAYD') + insert(app, "conf/host.cert", 8, 'VQQHDA1UZXN0IExvY2FsaXR5MRowGAYDVQQKDBFPcmdhbml6YXRpb24gTmFtZTEh') + insert(app, "conf/host.cert", 9, 'MB8GA1UECwwYT3JnYW5pemF0aW9uYWwgVW5pdCBOYW1lMRQwEgYDVQQDDAtDb21t') + insert(app, "conf/host.cert", 10, 'b24gTmFtZTEhMB8GCSqGSIb3DQEJARYSdGVzdEBlbWFpbC5hZGRyZXNzMIIBIjAN') + insert(app, "conf/host.cert", 11, 'BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOx9pCR7rZ50S9FotKVD2+aC36Hj') + insert(app, "conf/host.cert", 12, '4TkXZTZwEnh/fWyuiH2OPaj/dTw60Jvll4jshlnRHfJ6yfc/o7YlDUanLrQJm7I3') + insert(app, "conf/host.cert", 13, '/t3YNgqYg3WXeUTl+GrNHn/3QgFGYqKobu8kfrwP4IapQRqlq4ZSdlR/bWpxnYSC') + insert(app, "conf/host.cert", 14, 'ZoXeeoimoSUcLlqD5dw77v2BlG2gqL5+lr5Fx4mDC12vczoUMRg88+VuA1ezU4cu') + insert(app, "conf/host.cert", 15, 'XDe2MbpJMd7rqGN0xK4bCwkFtSJqBM1TH/Czr1S52hKrDTTys9PVw+eZSKO7BCk+') + insert(app, "conf/host.cert", 16, 'PDq5jjx337XOWiO0kSHfV64x68xTojfzTzF304byr2Ytq6DjNbpZKwdYBwIDAQAB') + insert(app, "conf/host.cert", 17, 'MA0GCSqGSIb3DQEBCwUAA4IBAQAw+cuEp3wbLcTIzKCrZ7KzH3zaMtzIU5ZAjTkt') + insert(app, "conf/host.cert", 18, '66QSFALq/ZvAswAybpWKb+2EZZ8iV477W0nFJUkHIOrOav4qWJfmPtdp2k6d2Eey') + insert(app, "conf/host.cert", 19, 'cYQjrD9ghV7aKtKCstFdXo4h23FNaKb+kHSXjvEuf8EuDWilXKrjczmJAmGpBeSE') + insert(app, "conf/host.cert", 20, 'nUVGGYYMAKf+ndkuSYYnJs/V823o9npSiy0Ke83Z64Co04+yos+BMIuDIhP/+LOp') + insert(app, "conf/host.cert", 21, 'pesqro66VwKswcG9O/sjSCaiFgljlQARB4xKBSwR5py8hKDBKfoWnvCpaFPLS34P') + insert(app, "conf/host.cert", 22, 'rGPQp900aMtDjORTe2ZP2EP/rMSm7w/PL8djNVMtgFKzY2Tc') + insert(app, "conf/host.cert", 23, '-----END CERTIFICATE-----') + + + # Run the newly created application + step('Run our ssl-application') + + self.play = callPlay(self, ['run', app]) + #wait for play to be ready + self.assert_(waitFor(self.play, 'Listening for HTTPS on port 9000')) + + step("Send request to https") + + browser = mechanize.Browser() + response = browser.open('https://localhost:9000/') + + step("check that ssl message is logged") + self.assert_(waitFor(self.play, 'I am ssl secured!')) + + step("stop play") + killPlay('https') + self.play.wait() + + #now we're going to manually configure log4j to log debug messages + step('using key file with password') + + insert(app, "conf/application.conf", 236, + 'certificate.key.file = conf/host.pass.key') + + # re-run the application with new setting + step('re-run our ssl-application') + + self.play = callPlay(self, ['run', app]) + #wait for play to be ready + self.assert_(waitFor(self.play, 'Listening for HTTPS on port 9000')) + + step("Send request to https") + + browser = mechanize.Browser() + response = browser.open('https://localhost:9000/') + + step("check that ssl message is logged") + self.assert_(waitFor(self.play, 'I am ssl secured!')) + + step("stop play") + killPlay('https') + self.play.wait() + + step("done testing ssl config") + def testLogLevelsAndLog4jConfig(self): # Testing job developing @@ -80,8 +239,7 @@ def testLogLevelsAndLog4jConfig(self): insert(app, "conf/log4j.xml", 15, ' ') insert(app, "conf/log4j.xml", 16, ' ') insert(app, "conf/log4j.xml", 17, '') - - + # Run the newly created application step('re-run our logger-application') @@ -689,9 +847,9 @@ def timeout(process): killPlay() timeoutOccurred = True -def killPlay(): +def killPlay(http = 'http'): try: - urllib2.urlopen('http://localhost:9000/@kill') + urllib2.urlopen('%s://localhost:9000/@kill' % http) except: pass @@ -748,4 +906,13 @@ def rename(app, fro, to): os.rename(os.path.join(app, fro), os.path.join(app, to)) if __name__ == '__main__': + # thanks to: https://stackoverflow.com/a/35960702/3221476 + try: + _create_unverified_https_context = ssl._create_unverified_context + except AttributeError: + # Legacy Python that doesn't verify HTTPS certificates by default + pass + else: + # Handle target environment that doesn't support HTTPS verification + ssl._create_default_https_context = _create_unverified_https_context unittest.main()