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 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 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 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 as a filepath to be loaded. The path is relative to the test directory
file. If the value is an undecorated string, that's the value. 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 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 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 If a ``-x`` or ``--failfast`` argument is provided then ``gabbi-run`` will
exit after the first test failure. 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): def _load_data_file(self, filename):
"""Read a file from the current test directory.""" """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: with open(path, mode='rb') as data_file:
return data_file.read() return data_file.read()

View File

@ -14,6 +14,7 @@
import argparse import argparse
from importlib import import_module from importlib import import_module
import os
import sys import sys
import unittest import unittest
@ -57,6 +58,9 @@ def run():
gabbi-run -x example.com:9999 /mountpoint < mytest.yaml 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 Multiple files may be named as arguments, separated from other arguments
by a ``--``. Each file will be run as a separate test suite:: 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) handler_objects = initialize_handlers(args.response_handlers)
verbosity = args.verbosity
failfast = args.failfast failfast = args.failfast
failure = False failure = False
if not input_files: if not input_files:
success = run_suite(sys.stdin, handler_objects, host, port, success = run_suite(sys.stdin, handler_objects, host, port,
prefix, force_ssl, failfast) prefix, force_ssl, failfast, verbosity)
failure = not success failure = not success
else: else:
for input_file in input_files: for input_file in input_files:
with open(input_file, 'r') as fh: with open(input_file, 'r') as fh:
data_dir = os.path.dirname(input_file)
success = run_suite(fh, handler_objects, host, port, 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 if not failure: # once failed, this is considered immutable
failure = not success failure = not success
if failure and failfast: if failure and failfast:
@ -95,7 +102,7 @@ def run():
def run_suite(handle, handler_objects, host, port, prefix, force_ssl=False, 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.""" """Run the tests from the YAML in handle."""
data = utils.load_yaml(handle) data = utils.load_yaml(handle)
if force_ssl: if force_ssl:
@ -103,10 +110,15 @@ def run_suite(handle, handler_objects, host, port, prefix, force_ssl=False,
data['defaults']['ssl'] = True data['defaults']['ssl'] = True
else: else:
data['defaults'] = {'ssl': True} data['defaults'] = {'ssl': True}
if verbosity:
if 'defaults' in data:
data['defaults']['verbose'] = verbosity
else:
data['defaults'] = {'verbose': verbosity}
loader = unittest.defaultTestLoader loader = unittest.defaultTestLoader
test_suite = suitemaker.test_suite_from_dict( 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) handlers=handler_objects)
result = ConciseTestRunner( result = ConciseTestRunner(
@ -194,6 +206,12 @@ def _make_argparser():
help='Custom response handler. Should be an import path of the ' help='Custom response handler. Should be an import path of the '
'form package.module or package.module:class.' '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 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 import exception
from gabbi.handlers import base from gabbi.handlers import base
from gabbi.handlers.jsonhandler import JSONHandler
from gabbi import runner from gabbi import runner
from gabbi.tests.simple_wsgi import SimpleWsgi from gabbi.tests.simple_wsgi import SimpleWsgi
@ -249,6 +250,77 @@ class RunnerTest(unittest.TestCase):
self.assertIn('{\n', output) self.assertIn('{\n', output)
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): def assertSuccess(self, exitError):
errors = exitError.args[0] errors = exitError.args[0]
if errors: if errors: