Merge branch 'master' into fix-pytest-yield

This commit is contained in:
Chris Dent 2016-11-28 20:19:29 +00:00
commit d2175fa89b
9 changed files with 201 additions and 7 deletions

View File

@ -269,8 +269,9 @@ flexibility when doing a ``POST`` or ``PUT``. If the value is not a
string (that is, it is a sequence or structure) it is treated as a
data structure which is turned into a JSON string. If the value is a
string that begins with ``<@`` then the rest of the string is treated
as the name of a file to be loaded from the same directory as the YAML
file. If the value is an undecorated string, that's the value.
as a filepath to be loaded. The path is relative to the test directory
and may not traverse up into parent directories. If the value is an
undecorated string, that's the value.
When reading from a file care should be taken to ensure that a
reasonable content-type is set for the data as this will control if any

View File

@ -50,3 +50,6 @@ YAML will default to ``ssl: True``.
If a ``-x`` or ``--failfast`` argument is provided then ``gabbi-run`` will
exit after the first test failure.
Use ``-v`` or ``--verbose`` with a value of ``all``, ``headers`` or ``body``
to turn on :ref:`verbosity <metadata>` for all tests being run.

View File

@ -243,7 +243,13 @@ class HTTPTestCase(testtools.TestCase):
def _load_data_file(self, filename):
"""Read a file from the current test directory."""
path = os.path.join(self.test_directory, os.path.basename(filename))
path = os.path.join(self.test_directory, filename)
has_dir_traversal = os.path.relpath(
path, start=self.test_directory).startswith(os.pardir)
if has_dir_traversal:
raise ValueError(
'Attempted loading of data file outside test directory: %s'
% filename)
with open(path, mode='rb') as data_file:
return data_file.read()

View File

@ -14,6 +14,7 @@
import argparse
from importlib import import_module
import os
import sys
import unittest
@ -57,6 +58,9 @@ def run():
gabbi-run -x example.com:9999 /mountpoint < mytest.yaml
Use `-v` or `--verbose` with a value of `all`, `headers` or `body` to
turn on verbosity for all tests being run.
Multiple files may be named as arguments, separated from other arguments
by a ``--``. Each file will be run as a separate test suite::
@ -74,18 +78,21 @@ def run():
handler_objects = initialize_handlers(args.response_handlers)
verbosity = args.verbosity
failfast = args.failfast
failure = False
if not input_files:
success = run_suite(sys.stdin, handler_objects, host, port,
prefix, force_ssl, failfast)
prefix, force_ssl, failfast, verbosity)
failure = not success
else:
for input_file in input_files:
with open(input_file, 'r') as fh:
data_dir = os.path.dirname(input_file)
success = run_suite(fh, handler_objects, host, port,
prefix, force_ssl, failfast)
prefix, force_ssl, failfast, data_dir,
verbosity)
if not failure: # once failed, this is considered immutable
failure = not success
if failure and failfast:
@ -95,7 +102,7 @@ def run():
def run_suite(handle, handler_objects, host, port, prefix, force_ssl=False,
failfast=False):
failfast=False, data_dir='.', verbosity=False):
"""Run the tests from the YAML in handle."""
data = utils.load_yaml(handle)
if force_ssl:
@ -103,10 +110,15 @@ def run_suite(handle, handler_objects, host, port, prefix, force_ssl=False,
data['defaults']['ssl'] = True
else:
data['defaults'] = {'ssl': True}
if verbosity:
if 'defaults' in data:
data['defaults']['verbose'] = verbosity
else:
data['defaults'] = {'verbose': verbosity}
loader = unittest.defaultTestLoader
test_suite = suitemaker.test_suite_from_dict(
loader, 'input', data, '.', host, port, None, None, prefix=prefix,
loader, 'input', data, data_dir, host, port, None, None, prefix=prefix,
handlers=handler_objects)
result = ConciseTestRunner(
@ -194,6 +206,12 @@ def _make_argparser():
help='Custom response handler. Should be an import path of the '
'form package.module or package.module:class.'
)
parser.add_argument(
'-v', '--verbose',
dest='verbosity',
choices=['all', 'body', 'headers'],
help='Turn on test verbosity for all tests run in this session.'
)
return parser

View File

@ -0,0 +1 @@
{"items": {"house": "blue"}}

View File

@ -0,0 +1,8 @@
tests:
- name: POST data from file
verbose: true
POST: /
request_headers:
content-type: application/json
data: <@subdir/sample.json

View File

@ -0,0 +1,8 @@
tests:
- name: simple data post
POST: /
request_headers:
content-type: application/json
data:
cat: poppy

View File

@ -0,0 +1,77 @@
#
# Licensed 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.
"""Test loading data from files with <@.
"""
import unittest
from gabbi import case
from six.moves import mock
@mock.patch(
'gabbi.case.open',
new_callable=mock.mock_open,
read_data='dummy content',
create=True,
)
class DataFileTest(unittest.TestCase):
"""Reading from local file is only allowed at or below the
test_directory level.
"""
def setUp(self):
self.http_case = case.HTTPTestCase('test_request')
def _assert_content_read(self, filepath):
self.assertEqual(
'dummy content', self.http_case._load_data_file(filepath))
def test_load_file(self, m_open):
self.http_case.test_directory = '.'
self._assert_content_read('data.json')
m_open.assert_called_with('./data.json', mode='rb')
def test_load_file_in_directory(self, m_open):
self.http_case.test_directory = '.'
self._assert_content_read('a/b/c/data.json')
m_open.assert_called_with('./a/b/c/data.json', mode='rb')
def test_load_file_in_root(self, m_open):
self.http_case.test_directory = '.'
filepath = '/top-level.private'
with self.assertRaises(ValueError):
self.http_case._load_data_file(filepath)
self.assertFalse(m_open.called)
def test_load_file_in_parent_dir(self, m_open):
self.http_case.test_directory = '.'
filepath = '../file-in-parent-dir.txt'
with self.assertRaises(ValueError):
self.http_case._load_data_file(filepath)
self.assertFalse(m_open.called)
def test_load_file_within_test_directory(self, m_open):
self.http_case.test_directory = '/a/b/c'
self._assert_content_read('../../b/c/file-in-test-dir.txt')
m_open.assert_called_with(
'/a/b/c/../../b/c/file-in-test-dir.txt', mode='rb')
def test_load_file_not_within_test_directory(self, m_open):
self.http_case.test_directory = '/a/b/c'
filepath = '../../b/E/file-in-test-dir.txt'
with self.assertRaises(ValueError):
self.http_case._load_data_file(filepath)
self.assertFalse(m_open.called)

View File

@ -22,6 +22,7 @@ from wsgi_intercept.interceptor import Urllib3Interceptor
from gabbi import exception
from gabbi.handlers import base
from gabbi.handlers.jsonhandler import JSONHandler
from gabbi import runner
from gabbi.tests.simple_wsgi import SimpleWsgi
@ -249,6 +250,77 @@ class RunnerTest(unittest.TestCase):
self.assertIn('{\n', output)
self.assertIn('}\n', output)
def test_data_dir_good(self):
"""Confirm that data dir is the test file's dir."""
sys.argv = ['gabbi-run', 'http://%s:%s/foo' % (self.host, self.port)]
sys.argv.append('--')
sys.argv.append('gabbi/tests/gabbits_runner/test_data.yaml')
with self.server():
try:
runner.run()
except SystemExit as err:
self.assertSuccess(err)
# Compare the verbose output of tests with pretty printed
# data.
with open('gabbi/tests/gabbits_runner/subdir/sample.json') as data:
data = JSONHandler.loads(data.read())
expected_string = JSONHandler.dumps(data, pretty=True)
sys.stdout.seek(0)
output = sys.stdout.read()
self.assertIn(expected_string, output)
def _run_verbosity_arg(self):
sys.argv.append('--')
sys.argv.append('gabbi/tests/gabbits_runner/verbosity.yaml')
with self.server():
try:
runner.run()
except SystemExit as err:
self.assertSuccess(err)
sys.stdout.seek(0)
output = sys.stdout.read()
return output
def test_verbosity_arg_none(self):
"""Confirm --verbose handling."""
sys.argv = ['gabbi-run', 'http://%s:%s/foo' % (self.host, self.port)]
output = self._run_verbosity_arg()
self.assertEqual('', output)
def test_verbosity_arg_body(self):
"""Confirm --verbose handling."""
sys.argv = ['gabbi-run', 'http://%s:%s/foo' % (self.host, self.port),
'--verbose=body']
output = self._run_verbosity_arg()
self.assertIn('{\n "cat": "poppy"\n}', output)
self.assertNotIn('application/json', output)
def test_verbosity_arg_headers(self):
"""Confirm --verbose handling."""
sys.argv = ['gabbi-run', 'http://%s:%s/foo' % (self.host, self.port),
'--verbose=headers']
output = self._run_verbosity_arg()
self.assertNotIn('{\n "cat": "poppy"\n}', output)
self.assertIn('application/json', output)
def test_verbosity_arg_all(self):
"""Confirm --verbose handling."""
sys.argv = ['gabbi-run', 'http://%s:%s/foo' % (self.host, self.port),
'--verbose=all']
output = self._run_verbosity_arg()
self.assertIn('{\n "cat": "poppy"\n}', output)
self.assertIn('application/json', output)
def assertSuccess(self, exitError):
errors = exitError.args[0]
if errors: