You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jp...@apache.org on 2015/01/28 01:11:21 UTC

trafficserver git commit: Initial addition of new_tsqa tests

Repository: trafficserver
Updated Branches:
  refs/heads/master 6ef00b89a -> 55ee77589


Initial addition of new_tsqa tests

This includes ports of the old TSQA tests as well as some basic keepalive tests.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/55ee7758
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/55ee7758
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/55ee7758

Branch: refs/heads/master
Commit: 55ee7758969eda71f375d3211d975a5367301eaf
Parents: 6ef00b8
Author: Thomas Jackson <ja...@gmail.com>
Authored: Mon Dec 22 18:23:52 2014 -0800
Committer: James Peach <jp...@apache.org>
Committed: Tue Jan 27 16:11:13 2015 -0800

----------------------------------------------------------------------
 ci/new_tsqa/Makefile                |  43 ++++++
 ci/new_tsqa/TODO                    |   7 +
 ci/new_tsqa/files/cert.pem          |  20 +++
 ci/new_tsqa/files/key.pem           |  28 ++++
 ci/new_tsqa/requirements.txt        |   5 +
 ci/new_tsqa/tests/helpers.py        |  56 ++++++++
 ci/new_tsqa/tests/test_example.py   | 220 +++++++++++++++++++++++++++++++
 ci/new_tsqa/tests/test_keepalive.py | 152 +++++++++++++++++++++
 8 files changed, 531 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/Makefile
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/Makefile b/ci/new_tsqa/Makefile
new file mode 100644
index 0000000..a46a684
--- /dev/null
+++ b/ci/new_tsqa/Makefile
@@ -0,0 +1,43 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+.PHONY: test clean update
+
+VIRTUALENV_DIR = virtualenv
+
+# Run all tests.
+test: $(VIRTUALENV_DIR)
+	@source $(VIRTUALENV_DIR)/bin/activate && $(VIRTUALENV_DIR)/bin/nosetests -sv --logging-level=INFO
+
+# Scan and list the tests.
+list: $(VIRTUALENV_DIR)
+	@source $(VIRTUALENV_DIR)/bin/activate && $(VIRTUALENV_DIR)/bin/nosetests -v --collect-only
+
+# Construct the virtualenv.
+$(VIRTUALENV_DIR):
+	@if [ ! -d $(VIRTUALENV_DIR) ]; then\
+		virtualenv $(VIRTUALENV_DIR);\
+	fi
+	@# Update the virtualenv with your dep libraries
+	source $(VIRTUALENV_DIR)/bin/activate && $(VIRTUALENV_DIR)/bin/pip install -r requirements.txt
+	@echo "Virtualenv ready!"
+
+# Update the virtualenv with the latest TSQA package and dependencies.
+update: $(VIRTUALENV_DIR)
+	source $(VIRTUALENV_DIR)/bin/activate && $(VIRTUALENV_DIR)/bin/pip install --upgrade -r requirements.txt
+
+clean:
+	rm -rf virtualenv

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/TODO
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/TODO b/ci/new_tsqa/TODO
new file mode 100644
index 0000000..c01205b
--- /dev/null
+++ b/ci/new_tsqa/TODO
@@ -0,0 +1,7 @@
+# TODO list for tsqa
+
+- runtests script
+    - run specific things within the virtualenv
+    - package up output from specific tests in a relocateable way
+- Documentation/Examples of TSQA framework
+- pylint for test cases

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/files/cert.pem
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/files/cert.pem b/ci/new_tsqa/files/cert.pem
new file mode 100644
index 0000000..fcac091
--- /dev/null
+++ b/ci/new_tsqa/files/cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVzCCAj+gAwIBAgIJAOY9Arrh4/IgMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNV
+BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
+Q29tcGFueSBMdGQwHhcNMTUwMTIyMDE1MjU3WhcNMTUwMjIxMDE1MjU3WjBCMQsw
+CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh
+dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+08GU61mR18JO9X20utgemoeeYyKx+LXZYQBc0cKwHzZIiYfokwCkCNekMjZ87DT2
++++8lBf3PatSgtA8/xanr8+TTDbKPehqdItDAy9e/xYgPBz9RXHuBUeOw+CPxt2e
+aGrGwy6ybW3jne/+vm73wn+ZzldpwGGXwIQAS9lFqtmisx/DftL8fhzpfp/uIU/K
+Y33iMiPpEHi8CHrOsaREl787ipKoqfxs+d1JNTHu1I+wJKgppOrtyjF1AjYDmrRg
+RO8rJqIaUKS+8teV2KazwfdPkgNyaoZO7NCPPEjWkbp2c+2AJQqCSyZmJ63idgkR
+msaSjRx45vJPOU/KFVHLuQIDAQABo1AwTjAdBgNVHQ4EFgQUtL1CTVRABxDQVbZy
+WwOZWMCs08QwHwYDVR0jBBgwFoAUtL1CTVRABxDQVbZyWwOZWMCs08QwDAYDVR0T
+BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAComsgXv9V7utk6yY1XV+rtjZmyRV
+758Jjzb2sqqVtw8jtEvdiO6rK+Chb49cAcBGJFHZL2/CJ6BWSOf79fLj/IGKC/nX
+UBz0dxrlg9x/KR/Jtp0qqQXIw/HT/NvaytYxMIBKqkmjG+kWiPn61dvwFjIERPOb
+xM4lHhaO/PKWDDVx6Sf7UzMalmwFjaGQFXCNM5dfqvdqDYYrbZwEWuqmxNy1sZBY
+SfY7Tyz6OP9NnlgtWRAITPqBS2kx/MVCGd2TtzJcJDxKK67tr0QFenGtXSZy555Q
+bNKjXKVWiHrVCEgttPri22o7Ax1Q6FpLHMXDIiveUl6aXq4ulNzRqXpmaw==
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/files/key.pem
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/files/key.pem b/ci/new_tsqa/files/key.pem
new file mode 100644
index 0000000..fddcacb
--- /dev/null
+++ b/ci/new_tsqa/files/key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTwZTrWZHXwk71
+fbS62B6ah55jIrH4tdlhAFzRwrAfNkiJh+iTAKQI16QyNnzsNPb777yUF/c9q1KC
+0Dz/Fqevz5NMNso96Gp0i0MDL17/FiA8HP1Fce4FR47D4I/G3Z5oasbDLrJtbeOd
+7/6+bvfCf5nOV2nAYZfAhABL2UWq2aKzH8N+0vx+HOl+n+4hT8pjfeIyI+kQeLwI
+es6xpESXvzuKkqip/Gz53Uk1Me7Uj7AkqCmk6u3KMXUCNgOatGBE7ysmohpQpL7y
+15XYprPB90+SA3Jqhk7s0I88SNaRunZz7YAlCoJLJmYnreJ2CRGaxpKNHHjm8k85
+T8oVUcu5AgMBAAECggEBANFqt8kNGtPDIW1c9Vh3FcUDbFtkW5e42BM7VZBItv8X
+IyOIWjTPRGpOQN87zc2YD85WaCwZi3TcsswV/szTbeDMK0MLSHVzHZzGgO5scclZ
+62Un0j5Uju1/uCv1MJueXuOq/YjX7LOWIq32Q/u3KKWcpdJP1pDgs0A8C0L3zBNK
+PjxnCO0FvJdcpqajEhtepYyTQAtWm/igWbuFgUcfZ55HTOBfBiLdACh6anbCdDJ8
+f2COFRrKu9Gn9mVyRirbyCa4B3VSj4R+WlKsc3erR2vNiEdJLd9x5OK7ZvMFHTvG
+V4BhWt3ffSBRIaVi0pIpYekWbnXjbqY6zjchiy3ruOUCgYEA6W4yBrbExJmelXCj
+dPOp5Ds/uAYaq9TkRLWzX837swPNh1+XJ9xGNgn4d5DbikN1xSdsJO/1dpwk3Uxg
+qE/tEvA2gip/DaxIcj3PfoPtFyebgZItvs5k97zGw9n0bgqoRAezzUl4Guz4AQVV
+Xz+3gICN1lFhRqxKm7Pt8Kc3D5cCgYEA6DrpLJCzOEd8qlhm6w7UGruBRA+QLACu
+zlqzkf4rw1vaXx4cP8ctoCiVWUIsPI0mD2sQvtXAPT8KzZqh3UCu1zyyochyCuVg
+C3fBQiSDtUb2Uk6u7fNFrn36oN7W/Q+sarJvIIECR1PjEGuT3eJppQgJB/VGUZqa
+OQJyTJPXaS8CgYEAskz8o0o51F3u1wEZqbxw+acUDbGD79qGncEYiUZiSqPN+uhW
+IhlL+/zzsAiS2PKcY4KwRSqRGQ89zVeIwSeD06JuUFC7iaseDz0NX/rPP49+ZaNN
+k+A9GUo1nOW/oco8KvKjMVw8BH0bFlSHmGCn/tyy+pBguEXkGzh9uANRuHMCgYAM
+TZKs2b2k7aSdIbHSIib6g5SFlo18x0x7gjKhOWX4I5WeFGpKtrKkGYJQCEFvs8qg
+ZnusoIZeuEhKPDb3EcYxgPW1vHjOOvirotyKNIUFxYynL6P01z6J0ALHIwcgwQPR
+Y0Kf5jXIsZkF9a0PxD70j0hrM4NWL2qcOpTzmaF/4QKBgFYQVrrI6YBxexKQ4J/m
+tG/OKlxef6mzrctu7RJGxzt9ag1IgOi10BMCIKCW7tfvhzzLuBiJ0imEGe+MYrau
+yIWCOVpmwcSnww8bV/25oqRxduVwZzmtZdUJbNSdiZ7jq4tsV9a8TZXts45veSFQ
+X+HWsoFkRoYLOx96mqN94/ZS
+-----END PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/requirements.txt
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/requirements.txt b/ci/new_tsqa/requirements.txt
new file mode 100644
index 0000000..a6aef76
--- /dev/null
+++ b/ci/new_tsqa/requirements.txt
@@ -0,0 +1,5 @@
+# requirements for the python virtualenv
+
+# TODO: pin a specific version
+https://github.com/jacksontj/tsqa/archive/master.zip
+pyyaml

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/tests/helpers.py
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/tests/helpers.py b/ci/new_tsqa/tests/helpers.py
new file mode 100644
index 0000000..82ef067
--- /dev/null
+++ b/ci/new_tsqa/tests/helpers.py
@@ -0,0 +1,56 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+import os
+import tempfile
+
+import tsqa.environment
+import tsqa.test_cases
+import tsqa.utils
+
+unittest = tsqa.utils.import_unittest()
+
+# TODO: check that the given path is relative
+def tests_file_path(path):
+    '''
+    Return the absolute path to a file with relative path "name" from tsqa/files
+    '''
+    base = os.path.realpath(os.path.join(__file__, '..', '..', 'files'))
+    return os.path.join(base, path)
+
+class EnvironmentCase(tsqa.test_cases.EnvironmentCase):
+    '''
+    This class will get an environment (which is unique) but won't start it
+    '''
+    @classmethod
+    def getEnv(cls):
+        '''
+        This function is responsible for returning an environment
+        '''
+        SOURCE_DIR = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..'))
+        TMP_DIR = os.path.join(tempfile.gettempdir(), 'tsqa')
+        ef = tsqa.environment.EnvironmentFactory(SOURCE_DIR,
+                                                 os.path.join(TMP_DIR, 'base_envs'),
+                                                 default_configure={'enable-example-plugins': None,
+                                                                    'enable-test-tools': None,
+                                                                    'enable-example-plugins': None,
+                                                                    },
+                                                 )
+        # TODO: figure out a way to determine why the build didn't fail and
+        # not skip all build failures?
+        try:
+            return ef.get_environment(cls.environment_factory['configure'], cls.environment_factory['env'])
+        except Exception as e:
+            raise unittest.SkipTest(e)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/tests/test_example.py
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/tests/test_example.py b/ci/new_tsqa/tests/test_example.py
new file mode 100644
index 0000000..99bba07
--- /dev/null
+++ b/ci/new_tsqa/tests/test_example.py
@@ -0,0 +1,220 @@
+'''
+Some example tests of the new tsqa
+'''
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+import requests
+import time
+
+import helpers
+
+import tsqa.test_cases
+import tsqa.utils
+
+# you can also skip (or conditionally skip) tests
+@helpers.unittest.skip('Not running TestNoOp, as it is a NoOp test')
+class TestNoOp(helpers.EnvironmentCase):
+    '''
+    This is purely a documentation test
+    '''
+    # you can set configure/environment options for the source build here
+    environment_factory = {
+        'configure': {# A value of None means that the argument has no value
+                      'enable-spdy': None,
+                      # if there is a value it will be converted to --key=value
+                      'with-max-api-stats': 2048,
+                      },
+        'env': None,
+    }
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This funciton is responsible for setting up the environment for this fixture
+        This includes everything pre-daemon start.
+
+        You are passed in cls (which is the instance of this class) and env (which
+        is an environment object)
+        '''
+        # we can modify any/all configs (note: all pre-daemon start)
+        cls.configs['remap.config'].add_line('map / http://http://trafficserver.readthedocs.org/')
+
+        # Some configs have nicer wrapper objects to give you a more pythonic interface
+        cls.configs['records.config']['CONFIG'].update({
+            'proxy.config.log.squid_log_enabled': 1,
+            'proxy.config.log.squid_log_is_ascii': 1,
+        })
+
+    def test_something(self):
+        '''
+        All functions beginning with "test_" will be run as tests for the class.
+        Within these functions your environment is already set up and started--
+        you only need to excercise the code that you intend to test
+        '''
+        # for example, you could send a request to ATS and check the response
+        ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']))
+
+        self.assertEqual(ret.status_code, 404)
+        self.assertIn('ATS', ret.headers['server'])
+
+
+class TestConfigureFlags(helpers.EnvironmentCase):
+    environment_factory = {
+        'configure': {'enable-spdy': None},
+        'env': None,
+    }
+
+    def test_spdy(self):
+        self.assertTrue(True)
+
+
+class TestBootstrap(helpers.EnvironmentCase):
+    def test_default_404(self):
+        ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']))
+
+        self.assertEqual(ret.status_code, 404)
+        self.assertIn('ATS', ret.headers['server'])
+
+    # TODO: re-enable once traffic_manager works
+    @helpers.unittest.skip('TSQA is currently unable to run traffic_cop or traffic_manager')
+    def test_trafficline(self):
+        cmd = [os.path.join(self.environment.layout.bindir, 'traffic_line'),
+               '-m',
+               'proxy.config',
+               ]
+        stdout, stderr = tsqa.utils.run_sync_command(cmd)
+        raise Exception(stdout)
+
+
+# TODO: enable once traffic_cop works (jpeach's fix for proxy.config.admin.user_id)
+'''
+class TestServerIntercept(helpers.EnvironmentCase, tsqa.test_cases.DynamicHTTPEndpointCase):
+    endpoint_port = 60000
+    @classmethod
+    def setUpEnv(cls, env):
+        cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}'.format(cls.endpoint_port))
+
+        cls.configs['plugin.config'].add_line('intercept.so')
+
+        def hello(request):
+            return 'hello'
+        cls.http_endpoint.add_handler('/', hello)
+
+
+    def test_basic_intercept(self):
+        for _ in xrange(0, 10):
+            ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']))
+
+            self.assertEqual(ret.status_code, 200)
+            self.assertIn('ATS', ret.headers['server'])
+'''
+
+
+class TestLogs(helpers.EnvironmentCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This funciton is responsible for setting up the environment for this fixture
+        This includes everything pre-daemon start
+        '''
+        # only add server headers when there weren't any
+        cls.configs['records.config']['CONFIG'].update({
+            'proxy.config.diags.debug.tags': 'log-.*',
+            'proxy.config.diags.debug.enabled': 1,
+            'proxy.config.log.hostname': 'test',
+            'proxy.config.log.search_top_sites': 1,
+        })
+    def test_logs_exist(self):
+        # send some requests
+        for x in xrange(0, 10):
+            ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']))
+
+            self.assertEqual(ret.status_code, 404)
+            self.assertIn('ATS', ret.headers['server'])
+
+        # TODO: some better way to know when the logs where syncd
+        time.sleep(10)  # wait for logs to hit disk
+
+        # verify that the log files exist
+        # TODO: check for logs 'manager.log', 'traffic.out
+        for logfile in ('diags.log', 'error.log', 'squid.blog'):
+            logfile_path = os.path.join(self.environment.layout.logdir, logfile)
+            self.assertTrue(os.path.isfile(logfile_path), logfile_path)
+
+
+class TestLogRefCounting(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This funciton is responsible for setting up the environment for this fixture
+        This includes everything pre-daemon start
+        '''
+        cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/\n'.format(cls.http_endpoint.address[1]))
+
+        cls.configs['plugin.config'].add_lines([
+            'tcpinfo.so --log-file=tcpinfo1 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2',
+            'tcpinfo.so --log-file=tcpinfo2 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2',
+            'tcpinfo.so --log-file=tcpinfo3 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2',
+            'tcpinfo.so --log-file=tcpinfo4 --hooks=ssn_start,txn_start,send_resp_hdr,ssn_close,txn_close --log-level=2',
+        ])
+
+        # only add server headers when there weren't any
+        cls.configs['records.config']['CONFIG'].update({
+            'proxy.config.log.max_space_mb_for_logs': 10,
+            'proxy.config.log.max_space_mb_for_orphan_logs': 10,
+            'proxy.config.log.squid_log_enabled': 1,
+            'proxy.config.log.squid_log_is_ascii': 1,
+            'proxy.config.log.rolling_interval_sec': 60,
+            'proxy.config.log.rolling_size_mb': 1,
+            'proxy.config.log.max_space_mb_headroom': 1,
+            'proxy.config.log.max_secs_per_buffer': 1,
+        })
+
+    def test_logs_exist(self):
+        # send some requests
+        for x in xrange(0, 10):
+            ret = requests.get('http://127.0.0.1:{0}/'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']))
+
+            self.assertEqual(ret.status_code, 404)
+            self.assertIn('ATS', ret.headers['server'])
+
+        # TODO: some better way to know when the logs where syncd
+        time.sleep(10)  # wait for logs to hit disk
+
+        logfile_path = os.path.join(self.environment.layout.logdir, 'squid.log')
+        self.assertTrue(os.path.isfile(logfile_path), logfile_path)
+
+
+class TestDynamicHTTPEndpointCase(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This funciton is responsible for setting up the environment for this fixture
+        This includes everything pre-daemon start
+        '''
+        cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/\n'.format(cls.http_endpoint.address[1]))
+
+        # only add server headers when there weren't any
+        cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2
+
+    def test_basic_proxy(self):
+        ret = requests.get(self.endpoint_url('/test'),
+                           proxies=self.proxies,
+                           )
+        self.assertEqual(ret.status_code, 404)
+        self.assertIn('WSGIServer', ret.headers['server'])

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/55ee7758/ci/new_tsqa/tests/test_keepalive.py
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/tests/test_keepalive.py b/ci/new_tsqa/tests/test_keepalive.py
new file mode 100644
index 0000000..fc49338
--- /dev/null
+++ b/ci/new_tsqa/tests/test_keepalive.py
@@ -0,0 +1,152 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+import os
+import requests
+import time
+import logging
+
+import helpers
+
+import tsqa.test_cases
+import tsqa.utils
+import tsqa.endpoint
+
+log = logging.getLogger(__name__)
+
+import SocketServer
+class KeepaliveTCPHandler(SocketServer.BaseRequestHandler):
+    """
+    A subclass of RequestHandler which will count the number of requests
+    per tcp session
+    """
+
+    def handle(self):
+        num_requests = 0
+        # Receive the data in small chunks and retransmit it
+        while True:
+            num_requests += 1
+            data = self.request.recv(4096).strip()
+            if data:
+                log.info('sending data back to the client')
+            else:
+                log.info('Client disconnected')
+                break
+            body = str(num_requests)
+            resp = ('HTTP/1.1 200 OK\r\n'
+                    'Content-Length: {content_length}\r\n'
+                    'Content-Type: text/html; charset=UTF-8\r\n'
+                    'Connection: keep-alive\r\n'
+                    '\r\n'
+                    '{body}'.format(content_length=len(body), body=body))
+            self.request.sendall(resp)
+
+# TODO: test timeouts
+# https://issues.apache.org/jira/browse/TS-3312
+# https://issues.apache.org/jira/browse/TS-242
+
+
+class TestKeepAliveOutHTTP(helpers.EnvironmentCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This function is responsible for setting up the environment for this fixture
+        This includes everything pre-daemon start
+        '''
+        # create a socket server
+        cls.socket_server = tsqa.endpoint.SocketServerDaemon(KeepaliveTCPHandler)
+        cls.socket_server.start()
+        cls.socket_server.ready.wait()
+        cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/\n'.format(cls.socket_server.port))
+
+        # only add server headers when there weren't any
+        cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2
+        cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_out'] = 1
+        cls.configs['records.config']['CONFIG']['share_server_session'] = 2
+
+        # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues)
+        cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1
+        cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0
+
+    def test_KA_origin(self):
+        '''
+        Test that the origin does in fact support keepalive
+        '''
+        with requests.Session() as s:
+            url = 'http://127.0.0.1:{0}/'.format(self.socket_server.port)
+            for x in xrange(1, 10):
+                ret = s.get(url)
+                self.assertEqual(ret.status_code, 200)
+                self.assertEqual(ret.text.strip(), str(x))
+
+    def test_KA_proxy(self):
+        '''
+        Test that keepalive works through ATS to that origin
+        '''
+        url = 'http://127.0.0.1:{0}'.format(self.socket_server.port)
+        for x in xrange(1, 10):
+            ret = requests.get(url, proxies=self.proxies)
+            self.assertEqual(ret.status_code, 200)
+            self.assertEqual(ret.text.strip(), str(x))
+
+class TestKeepAliveOutHTTPS(helpers.EnvironmentCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This function is responsible for setting up the environment for this fixture
+        This includes everything pre-daemon start
+        '''
+        # create a socket server
+        cls.socket_server = tsqa.endpoint.SSLSocketServerDaemon(KeepaliveTCPHandler,
+                                             helpers.tests_file_path('cert.pem'),
+                                             helpers.tests_file_path('key.pem'))
+
+        cls.socket_server.start()
+        cls.socket_server.ready.wait()
+        cls.configs['remap.config'].add_line('map / https://127.0.0.1:{0}/\n'.format(cls.socket_server.port))
+
+        # only add server headers when there weren't any
+        cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2
+        cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_out'] = 1
+        cls.configs['records.config']['CONFIG']['share_server_session'] = 2
+
+        # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues)
+        cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1
+        cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0
+
+        cls.configs['records.config']['CONFIG']['proxy.config.ssl.number.threads'] = -1
+
+
+    def test_KA_origin(self):
+        '''
+        Test that the origin does in fact support keepalive
+        '''
+        with requests.Session() as s:
+            url = 'https://127.0.0.1:{0}/'.format(self.socket_server.port)
+            for x in xrange(1, 10):
+                ret = s.get(url, verify=False)
+                self.assertEqual(ret.status_code, 200)
+                self.assertEqual(ret.text.strip(), str(x))
+
+    def test_KA_proxy(self):
+        '''
+        Test that keepalive works through ATS to that origin
+        '''
+        url = 'http://127.0.0.1:{0}'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        for x in xrange(1, 10):
+            ret = requests.get(url)
+            self.assertEqual(ret.status_code, 200)
+            self.assertEqual(ret.text.strip(), str(x))