Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WORKAROUND kwarg custom_sentence in _get_match for working with outlines in hooks #554

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9362151
First version of lxc isolator without world restore is finished.
Feb 8, 2013
06c8d8d
Enabling lxc isolator in lettuce
Feb 8, 2013
9125611
Finished
Feb 11, 2013
8a395b2
Merge pull request #1 from uty/master
Feb 11, 2013
e0e3b18
little docstring added
Feb 15, 2013
b90c3db
Added options for selective file loading and file excluding
Feb 28, 2013
41111c6
Merge pull request #2 from uty/master
Feb 28, 2013
9d8839e
Added feature that allows to define .py files to load in first line o…
Mar 11, 2013
b5f7d5c
Improved files to load finding algorithm
Mar 12, 2013
27047b5
removed debug print
Mar 12, 2013
f563e74
Merge pull request #3 from uty/master
Mar 14, 2013
2288d7c
wrapped sys.stdout
Mar 18, 2013
22adea4
fixed terrain import
Mar 18, 2013
74ddf7f
Merge pull request #4 from uty/master
uty Mar 18, 2013
c675ca4
terrain is now imported from directory with feature file by default
Mar 19, 2013
493af33
Merge pull request #5 from uty/master
uty Mar 19, 2013
05c3d23
Fixed files to load search when base path is path to .feature file
Mar 19, 2013
960d8b2
Merge pull request #6 from uty/master
uty Mar 19, 2013
1a45932
removed debug info
Mar 19, 2013
4591cfc
Merge branch 'master' of github.com:Scalr/lettuce
Mar 19, 2013
7840711
Fixed files to load finding now parses feature file given in base path
Mar 19, 2013
1c15566
changed failfast behaviour now adding last failed step to list of ste…
uty Apr 2, 2013
ff60af6
Merge pull request #7 from uty/master
uty Apr 2, 2013
631eb2e
changed failfast behaviour now stops scenario execution instead of fa…
uty Apr 2, 2013
6af4d2a
Merge pull request #8 from uty/master
uty Apr 2, 2013
cf68fa7
sync with upstream
uty Apr 2, 2013
72a4cc1
Changed tag matching behaviour - now excluding tags have higher
Apr 29, 2013
af38849
Merge branch 'master' of github.com:Scalr/lettuce
Apr 29, 2013
f1cc8fa
Improved the way that time_took is shown - now always showing minutes…
Apr 29, 2013
17b902f
Added fallback to original functionality when feature dir have no
May 6, 2013
912019b
Fixed time displaying
May 7, 2013
6bddb6b
merge from upstream
Oct 15, 2013
66931fe
Merge pull request #9 from uty/master
uty Oct 15, 2013
3abb2e1
WORKAROUND Add custom_sentence kwarg to _get_match method for handlin…
Theramas Sep 13, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 75 additions & 14 deletions lettuce/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
# python 2.5 fallback
pass

from datetime import datetime
import random

from lettuce.core import Feature, TotalResult
Expand All @@ -43,7 +44,8 @@
from lettuce.exceptions import StepLoadingError
from lettuce.plugins import (
xunit_output,
autopdb
autopdb,
lxc_isolator
)
from lettuce import fs
from lettuce import exceptions
Expand All @@ -55,6 +57,23 @@
pass


# force flush calls so lettuce output could be read from pipe
class _Stdout(object):

def __init__(self):
self._obj = sys.stdout

def __getattr__(self, attr):
return getattr(self._obj, attr)

def write(self, s):
# mb flush every X bytes?
self._obj.write(s)
self._obj.flush()

sys.stdout = _Stdout()


__all__ = [
'after',
'before',
Expand All @@ -65,18 +84,41 @@
'call_hook',
]

try:
terrain = fs.FileSystem._import("terrain")
reload(terrain)
except Exception, e:
if not "No module named terrain" in str(e):
string = 'Lettuce has tried to load the conventional environment ' \
'module "terrain"\nbut it has errors, check its contents and ' \
'try to run lettuce again.\n\nOriginal traceback below:\n\n'

sys.stderr.write(string)
sys.stderr.write(exceptions.traceback.format_exc(e))
raise SystemExit(1)
terrain = None


def import_terrain(terrain_file="terrain"):
try:
global terrain
filename = os.path.basename(terrain_file)
dirname = os.path.dirname(terrain_file)
sys.path.insert(0, dirname)
terrain = __import__(filename.split('.')[0])
sys.path.remove(dirname)
# commented for possible restore
# terrain = fs.FileSystem._import(terrain_file)
# reload(terrain)
except Exception, e:
if not "No module named terrain" in str(e):
string = 'Lettuce has tried to load the conventional environment ' \
'module "terrain"\nbut it has errors, check its contents and ' \
'try to run lettuce again.\n\nOriginal traceback below:\n\n'

sys.stderr.write(string)
sys.stderr.write(exceptions.traceback.format_exc(e))
raise SystemExit(1)


plugins = []


def import_plugins(plugins_dir):
sys.path.insert(0, plugins_dir)
for filename in os.listdir(plugins_dir):
if not filename.startswith('_') and filename.endswith('.py'):
plugin = __import__(filename.split('.')[0])
plugins.append(plugin)


class Runner(object):
Expand All @@ -87,7 +129,8 @@ class Runner(object):
"""
def __init__(self, base_path, scenarios=None, verbosity=0, random=False,
enable_xunit=False, xunit_filename=None, tags=None,
failfast=False, auto_pdb=False):
failfast=False, auto_pdb=False, files_to_load=None,
excluded_files=None):
""" lettuce.Runner will try to find a terrain.py file and
import it from within `base_path`
"""
Expand All @@ -100,7 +143,9 @@ def __init__(self, base_path, scenarios=None, verbosity=0, random=False,
base_path = os.path.dirname(base_path)

sys.path.insert(0, base_path)
self.loader = fs.FeatureLoader(base_path)
self.loader = fs.FeatureLoader(base_path,
files_to_load,
excluded_files)
self.verbosity = verbosity
self.scenarios = scenarios and map(int, scenarios.split(",")) or None
self.failfast = failfast
Expand Down Expand Up @@ -133,6 +178,7 @@ def run(self):
""" Find and load step definitions, and them find and load
features under `base_path` specified on constructor
"""
started_at = datetime.now()
try:
self.loader.find_and_load_step_definitions()
except StepLoadingError, e:
Expand Down Expand Up @@ -185,4 +231,19 @@ def run(self):
if failed:
raise SystemExit(2)

finished_at = datetime.now()
time_took = finished_at - started_at

hours = time_took.seconds / 60 / 60
minutes = time_took.seconds / 60
seconds = time_took.seconds
if hours:
print "(finished within %d hours, %d minutes, %d seconds)" % \
(hours, minutes % 60, seconds % 60)
elif minutes:
print "(finished within %d minutes, %d seconds)" % \
(minutes, seconds % 60)
elif seconds:
print "(finished within %d seconds)" % seconds

return total
99 changes: 99 additions & 0 deletions lettuce/bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,39 @@
import optparse

import lettuce
from fs import FeatureLoader
from core import Language

FILES_TO_LOAD_HEADER = 'Using step definitions from:'


def find_files_to_load(path):
feature_files = None
if path.endswith('.feature'):
feature_files = [path]
else:
loader = FeatureLoader(path)
feature_files = loader.find_feature_files()

result = []
for f in feature_files:
with open(f, 'r') as fp:
while True:
line = fp.readline()
if line == '':
break

line = line.lstrip()
if line.startswith(Language.feature):
break

if line.startswith(FILES_TO_LOAD_HEADER):
files_to_load_str = line[len(FILES_TO_LOAD_HEADER):]
files = files_to_load_str.split(',')
result.extend([name.strip() for name in files])
break

return result


def main(args=sys.argv[1:]):
Expand Down Expand Up @@ -78,6 +111,42 @@ def main(args=sys.argv[1:]):
action="store_true",
help='Launches an interactive debugger upon error')

parser.add_option("--plugins-dir",
dest="plugins_dir",
default=None,
type="string",
help='Sets plugins directory')

parser.add_option("--terrain-file",
dest="terrain_file",
default=None,
type="string",
help='Sets terrain file')

parser.add_option("--files-to-load",
dest="files_to_load",
default=None,
type="string",
help='Usage: \n'
'lettuce some/dir --files-to-load file1[,file2[,file3...]]'
'\n'
'Defines list of .py files that needs to be loaded. '
'You can use regular expressions for filenames. '
'Use either this option or --excluded-files, '
'but not them both.')

parser.add_option("--excluded-files",
dest="excluded_files",
default=None,
type="string",
help='Usage: \n'
'lettuce some/dir --files-to-load file1[,file2[,file3...]]'
'\n'
'Defines list of .py files that should not be loaded. '
'You can use regular expressions for filenames. '
'Use either this option, or --files-to-load, '
'but not them both.')

options, args = parser.parse_args(args)
if args:
base_path = os.path.abspath(args[0])
Expand All @@ -91,6 +160,34 @@ def main(args=sys.argv[1:]):
if options.tags:
tags = [tag.strip('@') for tag in options.tags]

# Terrain file loading
feature_dir = base_path if not base_path.endswith('.feature') \
else os.path.dirname(base_path)
terrain_file = options.terrain_file or \
os.environ.get('LETTUCE_TERRAIN_FILE',
os.path.join(feature_dir, 'terrain'))
if not os.path.exists(terrain_file + '.py'):
terrain_file = 'terrain'
lettuce.import_terrain(terrain_file)

# Plugins loading
plugins_dir = options.plugins_dir or os.environ.get('LETTUCE_TERRAIN_FILE',
None)
if plugins_dir:
lettuce.import_plugins(options.plugins_dir)

# Find files to load that are defined in .feature file
files_to_load = None
excluded_files = None

if options.files_to_load:
files_to_load = options.files_to_load.split(',')
elif options.excluded_files:
excluded_files = options.excluded_files.split(',')
else:
files_to_load = find_files_to_load(base_path)

# Create and run lettuce runner instance
runner = lettuce.Runner(
base_path,
scenarios=options.scenarios,
Expand All @@ -101,6 +198,8 @@ def main(args=sys.argv[1:]):
failfast=options.failfast,
auto_pdb=options.auto_pdb,
tags=tags,
files_to_load=files_to_load,
excluded_files=excluded_files,
)

result = runner.run()
Expand Down
34 changes: 26 additions & 8 deletions lettuce/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,12 @@ def _parse_remaining_lines(self, lines):
keys, hashes = strings.parse_hashes(lines)
return keys, hashes, multiline

def _get_match(self, ignore_case):
def _get_match(self, ignore_case, custom_sentence=None):
matched, func = None, lambda: None

sentence = custom_sentence or self.sentence
for regex, func in STEP_REGISTRY.items():
matched = re.search(regex, self.sentence, ignore_case and re.I or 0)
matched = re.search(regex, sentence, ignore_case and re.I or 0)
if matched:
break

Expand Down Expand Up @@ -461,10 +462,15 @@ def run_all(steps, outline=None, run_callbacks=False, ignore_case=True, failfast
steps_undefined.append(e.step)

except Exception, e:
if failfast:
raise
steps_failed.append(step)
reasons_to_fail.append(step.why)
if failfast:
# raise
return (all_steps,
steps_passed,
steps_failed,
steps_undefined,
reasons_to_fail)

finally:
all_steps.append(step)
Expand Down Expand Up @@ -627,9 +633,14 @@ def matches_tags(self, tags):
matched = []

if isinstance(self.tags, list):
for tag in self.tags:
if tag in tags:
return True
match = False
for tag in tags:
if tag.startswith('-') and tag[1:] in self.tags:
return False
if tag in self.tags:
match = True
if match:
return True
else:
self.tags = []

Expand Down Expand Up @@ -1183,7 +1194,14 @@ def run(self, scenarios=None, ignore_case=True, tags=None, random=False, failfas
if not scenario.matches_tags(tags):
continue

scenarios_ran.extend(scenario.run(ignore_case, failfast=failfast))
if self.background:
self.background.run(ignore_case)

scenario_run_results = scenario.run(ignore_case, failfast=failfast)
scenarios_ran.extend(scenario_run_results)
any_outline_failed = any(s.steps_failed for s in scenario_run_results)
if failfast and any_outline_failed:
break
except:
if failfast:
call_hook('after_each', 'feature', self)
Expand Down
31 changes: 23 additions & 8 deletions lettuce/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,32 @@
class FeatureLoader(object):
"""Loader class responsible for findind features and step
definitions along a given path on filesystem"""
def __init__(self, base_dir):
def __init__(self, base_dir, files_to_load=None, excluded_files=None):
self.base_dir = FileSystem.abspath(base_dir)

def _normalize_filenames(file_list):
return [r'.*%s\.py$' % f.split('.')[0] for f in file_list]

# we can only have files_to_load or excluded_files, but not both
self.files_to_load = None
self.excluded_files = None
if files_to_load:
self.files_to_load = _normalize_filenames(files_to_load)
elif excluded_files:
self.excluded_files = _normalize_filenames(excluded_files)

def find_and_load_step_definitions(self):
# find steps, possibly up several directories
base_dir = self.base_dir
while base_dir != '/':
files = FileSystem.locate(base_dir, '*.py')
if files:
break
base_dir = FileSystem.join(base_dir, '..')
files = FileSystem.locate(self.base_dir, '*.py')

def _matches_any(str_, pattern_list):
return any(map(lambda p: re.match(p, str_), pattern_list))

if self.files_to_load:
is_file_wanted = lambda f: _matches_any(f, self.files_to_load)
files = filter(is_file_wanted, files)
elif self.excluded_files:
is_file_wanted = lambda f: not _matches_any(f, self.excluded_files)
files = filter(is_file_wanted, files)

for filename in files:
root = FileSystem.dirname(filename)
Expand Down
Loading