Commit 86dd4e89 by Gabriel Falcão

Merge pull request #161 from chris-morgan/master

Code style improvements
parents 4e5d9e59 37d5a910
...@@ -32,6 +32,7 @@ from lettuce.exceptions import LettuceSyntaxError ...@@ -32,6 +32,7 @@ from lettuce.exceptions import LettuceSyntaxError
fs = FileSystem() fs = FileSystem()
class HashList(list): class HashList(list):
__base_msg = 'The step "%s" have no table defined, so ' \ __base_msg = 'The step "%s" have no table defined, so ' \
'that you can\'t use step.hashes.%s' 'that you can\'t use step.hashes.%s'
...@@ -64,6 +65,7 @@ class HashList(list): ...@@ -64,6 +65,7 @@ class HashList(list):
raise AssertionError(self.__base_msg % (self.step.sentence, 'last')) raise AssertionError(self.__base_msg % (self.step.sentence, 'last'))
class Language(object): class Language(object):
code = 'en' code = 'en'
name = 'English' name = 'English'
...@@ -73,6 +75,7 @@ class Language(object): ...@@ -73,6 +75,7 @@ class Language(object):
examples = 'Examples|Scenarios' examples = 'Examples|Scenarios'
scenario_outline = 'Scenario Outline' scenario_outline = 'Scenario Outline'
scenario_separator = 'Scenario( Outline)?' scenario_separator = 'Scenario( Outline)?'
def __init__(self, code=u'en'): def __init__(self, code=u'en'):
self.code = code self.code = code
for attr, value in languages.LANGUAGES[code].items(): for attr, value in languages.LANGUAGES[code].items():
...@@ -98,6 +101,7 @@ class Language(object): ...@@ -98,6 +101,7 @@ class Language(object):
return instance return instance
class StepDefinition(object): class StepDefinition(object):
"""A step definition is a wrapper for user-defined callbacks. It """A step definition is a wrapper for user-defined callbacks. It
gets a few metadata from file, such as filename and line number""" gets a few metadata from file, such as filename and line number"""
...@@ -122,6 +126,7 @@ class StepDefinition(object): ...@@ -122,6 +126,7 @@ class StepDefinition(object):
return ret return ret
class StepDescription(object): class StepDescription(object):
"""A simple object that holds filename and line number of a step """A simple object that holds filename and line number of a step
description (step within feature file)""" description (step within feature file)"""
...@@ -132,6 +137,7 @@ class StepDescription(object): ...@@ -132,6 +137,7 @@ class StepDescription(object):
self.line = line self.line = line
class ScenarioDescription(object): class ScenarioDescription(object):
"""A simple object that holds filename and line number of a scenario """A simple object that holds filename and line number of a scenario
description (scenario within feature file)""" description (scenario within feature file)"""
...@@ -146,6 +152,7 @@ class ScenarioDescription(object): ...@@ -146,6 +152,7 @@ class ScenarioDescription(object):
self.line = pline + 1 self.line = pline + 1
break break
class FeatureDescription(object): class FeatureDescription(object):
"""A simple object that holds filename and line number of a feature """A simple object that holds filename and line number of a feature
description""" description"""
...@@ -168,6 +175,7 @@ class FeatureDescription(object): ...@@ -168,6 +175,7 @@ class FeatureDescription(object):
self.description_at = tuple(described_at) self.description_at = tuple(described_at)
class Step(object): class Step(object):
""" Object that represents each step on feature files.""" """ Object that represents each step on feature files."""
has_definition = False has_definition = False
...@@ -198,8 +206,8 @@ class Step(object): ...@@ -198,8 +206,8 @@ class Step(object):
method_name = sentence method_name = sentence
groups = [ groups = [
('"', re.compile(r'("[^"]+")')), # double quotes ('"', re.compile(r'("[^"]+")')), # double quotes
("'", re.compile(r"('[^']+')")), # single quotes ("'", re.compile(r"('[^']+')")), # single quotes
] ]
attribute_names = [] attribute_names = []
...@@ -217,14 +225,11 @@ class Step(object): ...@@ -217,14 +225,11 @@ class Step(object):
method_name = method_name.replace(match, group_name) method_name = method_name.replace(match, group_name)
attribute_names.append(group_name) attribute_names.append(group_name)
method_name = unicodedata.normalize('NFKD', method_name) \ method_name = unicodedata.normalize('NFKD', method_name) \
.encode('ascii', 'ignore') .encode('ascii', 'ignore')
method_name = '%s(step%s)' % ( method_name = '%s(step%s)' % (
"_".join(re.findall("\w+", method_name)).lower(), "_".join(re.findall("\w+", method_name)).lower(),
attribute_names and (", %s" % ", ".join(attribute_names)) or "" attribute_names and (", %s" % ", ".join(attribute_names)) or "")
)
return method_name, sentence return method_name, sentence
...@@ -232,6 +237,7 @@ class Step(object): ...@@ -232,6 +237,7 @@ class Step(object):
sentence = self.sentence sentence = self.sentence
hashes = self.hashes[:] # deep copy hashes = self.hashes[:] # deep copy
for k, v in data.items(): for k, v in data.items():
def evaluate(stuff): def evaluate(stuff):
return stuff.replace(u'<%s>' % unicode(k), unicode(v)) return stuff.replace(u'<%s>' % unicode(k), unicode(v))
...@@ -387,7 +393,7 @@ class Step(object): ...@@ -387,7 +393,7 @@ class Step(object):
return True return True
@staticmethod @staticmethod
def run_all(steps, outline = None, run_callbacks = False, ignore_case = True): def run_all(steps, outline=None, run_callbacks=False, ignore_case=True):
"""Runs each step in the given list of steps. """Runs each step in the given list of steps.
Returns a tuple of five lists: Returns a tuple of five lists:
...@@ -434,7 +440,7 @@ class Step(object): ...@@ -434,7 +440,7 @@ class Step(object):
return (all_steps, steps_passed, steps_failed, steps_undefined, reasons_to_fail) return (all_steps, steps_passed, steps_failed, steps_undefined, reasons_to_fail)
@classmethod @classmethod
def many_from_lines(klass, lines, filename = None, original_string = None): def many_from_lines(klass, lines, filename=None, original_string=None):
"""Parses a set of steps from lines of input. """Parses a set of steps from lines of input.
This will correctly parse and produce a list of steps from lines without This will correctly parse and produce a list of steps from lines without
...@@ -652,9 +658,8 @@ class Scenario(object): ...@@ -652,9 +658,8 @@ class Scenario(object):
steps_skipped = filter(skip, all_steps) steps_skipped = filter(skip, all_steps)
if outline: if outline:
call_hook( call_hook('outline', 'scenario', self, order, outline,
'outline', 'scenario', self, order, outline, reasons_to_fail reasons_to_fail)
)
return ScenarioResult( return ScenarioResult(
self, self,
...@@ -662,8 +667,7 @@ class Scenario(object): ...@@ -662,8 +667,7 @@ class Scenario(object):
steps_failed, steps_failed,
steps_skipped, steps_skipped,
steps_undefined, steps_undefined,
True True)
)
if self.outlines: if self.outlines:
first = True first = True
...@@ -757,14 +761,15 @@ class Scenario(object): ...@@ -757,14 +761,15 @@ class Scenario(object):
with_file=with_file, with_file=with_file,
original_string=original_string, original_string=original_string,
language=language, language=language,
tags=tags tags=tags)
)
return scenario return scenario
class Feature(object): class Feature(object):
""" Object that represents a feature.""" """ Object that represents a feature."""
described_at = None described_at = None
def __init__(self, name, remaining_lines, with_file, original_string, def __init__(self, name, remaining_lines, with_file, original_string,
language=None, tags=None): language=None, tags=None):
...@@ -778,8 +783,7 @@ class Feature(object): ...@@ -778,8 +783,7 @@ class Feature(object):
self.scenarios, self.description = self._parse_remaining_lines( self.scenarios, self.description = self._parse_remaining_lines(
remaining_lines, remaining_lines,
original_string, original_string,
with_file with_file)
)
self.original_string = original_string self.original_string = original_string
...@@ -841,17 +845,12 @@ class Feature(object): ...@@ -841,17 +845,12 @@ class Feature(object):
found = len(re.findall(r'%s:[ ]*\w+' % language.feature, "\n".join(lines), re.U)) found = len(re.findall(r'%s:[ ]*\w+' % language.feature, "\n".join(lines), re.U))
if found > 1: if found > 1:
raise LettuceSyntaxError( raise LettuceSyntaxError(with_file,
with_file, 'A feature file must contain ONLY ONE feature!')
'A feature file must contain ONLY ONE feature!'
)
elif found == 0: elif found == 0:
raise LettuceSyntaxError( raise LettuceSyntaxError(with_file,
with_file, 'Features must have a name. e.g: "Feature: This is my name"')
'Features must have a name. e.g: "Feature: This is my name"'
)
while lines: while lines:
matched = re.search(r'%s:(.*)' % language.feature, lines[0], re.I) matched = re.search(r'%s:(.*)' % language.feature, lines[0], re.I)
...@@ -898,15 +897,12 @@ class Feature(object): ...@@ -898,15 +897,12 @@ class Feature(object):
parts = strings.split_scenarios(lines, scenario_prefix) parts = strings.split_scenarios(lines, scenario_prefix)
scenario_strings = [ scenario_strings = [u"%s" % (s) for s in parts if s.strip()]
u"%s" % (s) for s in parts if s.strip()
]
kw = dict( kw = dict(
original_string=original_string, original_string=original_string,
with_file=with_file, with_file=with_file,
language=self.language, language=self.language,
tags=self.tags tags=self.tags)
)
scenarios = [Scenario.from_string(s, **kw) for s in scenario_strings] scenarios = [Scenario.from_string(s, **kw) for s in scenario_strings]
...@@ -932,6 +928,7 @@ class Feature(object): ...@@ -932,6 +928,7 @@ class Feature(object):
call_hook('after_each', 'feature', self) call_hook('after_each', 'feature', self)
return FeatureResult(self, *scenarios_ran) return FeatureResult(self, *scenarios_ran)
class FeatureResult(object): class FeatureResult(object):
"""Object that holds results of each scenario ran from within a feature""" """Object that holds results of each scenario ran from within a feature"""
def __init__(self, feature, *scenario_results): def __init__(self, feature, *scenario_results):
...@@ -942,6 +939,7 @@ class FeatureResult(object): ...@@ -942,6 +939,7 @@ class FeatureResult(object):
def passed(self): def passed(self):
return all([result.passed for result in self.scenario_results]) return all([result.passed for result in self.scenario_results])
class ScenarioResult(object): class ScenarioResult(object):
"""Object that holds results of each step ran from within a scenario""" """Object that holds results of each step ran from within a scenario"""
def __init__(self, scenario, steps_passed, steps_failed, steps_skipped, def __init__(self, scenario, steps_passed, steps_failed, steps_skipped,
...@@ -966,6 +964,7 @@ class ScenarioResult(object): ...@@ -966,6 +964,7 @@ class ScenarioResult(object):
def failed(self): def failed(self):
return len(self.steps_failed) > 0 return len(self.steps_failed) > 0
class TotalResult(object): class TotalResult(object):
def __init__(self, feature_results): def __init__(self, feature_results):
self.feature_results = feature_results self.feature_results = feature_results
...@@ -973,7 +972,7 @@ class TotalResult(object): ...@@ -973,7 +972,7 @@ class TotalResult(object):
self.steps_passed = 0 self.steps_passed = 0
self.steps_failed = 0 self.steps_failed = 0
self.steps_skipped = 0 self.steps_skipped = 0
self.steps_undefined= 0 self.steps_undefined = 0
self._proposed_definitions = [] self._proposed_definitions = []
self.steps = 0 self.steps = 0
for feature_result in self.feature_results: for feature_result in self.feature_results:
...@@ -986,7 +985,6 @@ class TotalResult(object): ...@@ -986,7 +985,6 @@ class TotalResult(object):
self.steps += scenario_result.total_steps self.steps += scenario_result.total_steps
self._proposed_definitions.extend(scenario_result.steps_undefined) self._proposed_definitions.extend(scenario_result.steps_undefined)
def _filter_proposed_definitions(self): def _filter_proposed_definitions(self):
sentences = [] sentences = []
for step in self._proposed_definitions: for step in self._proposed_definitions:
......
...@@ -18,6 +18,7 @@ import re ...@@ -18,6 +18,7 @@ import re
from lettuce.core import STEP_REGISTRY from lettuce.core import STEP_REGISTRY
from lettuce.exceptions import StepLoadingError from lettuce.exceptions import StepLoadingError
def step(regex): def step(regex):
"""Decorates a function, so that it will become a new step """Decorates a function, so that it will become a new step
definition. definition.
......
...@@ -19,11 +19,13 @@ from os.path import join, dirname ...@@ -19,11 +19,13 @@ from os.path import join, dirname
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.conf import settings from django.conf import settings
def _filter_bultins(module): def _filter_bultins(module):
"returns only those apps that are not builtin django.contrib" "returns only those apps that are not builtin django.contrib"
name = module.__name__ name = module.__name__
return not name.startswith("django.contrib") and name != 'lettuce.django' return not name.startswith("django.contrib") and name != 'lettuce.django'
def _filter_configured_apps(module): def _filter_configured_apps(module):
"returns only those apps that are in django.conf.settings.LETTUCE_APPS" "returns only those apps that are in django.conf.settings.LETTUCE_APPS"
app_found = True app_found = True
...@@ -35,6 +37,7 @@ def _filter_configured_apps(module): ...@@ -35,6 +37,7 @@ def _filter_configured_apps(module):
return app_found return app_found
def _filter_configured_avoids(module): def _filter_configured_avoids(module):
"returns apps that are not within django.conf.settings.LETTUCE_AVOID_APPS" "returns apps that are not within django.conf.settings.LETTUCE_AVOID_APPS"
run_app = False run_app = False
...@@ -45,9 +48,11 @@ def _filter_configured_avoids(module): ...@@ -45,9 +48,11 @@ def _filter_configured_avoids(module):
return not run_app return not run_app
def get_apps(): def get_apps():
return map(import_module, settings.INSTALLED_APPS) return map(import_module, settings.INSTALLED_APPS)
def harvest_lettuces(only_the_apps=None, avoid_apps=None, path="features"): def harvest_lettuces(only_the_apps=None, avoid_apps=None, path="features"):
"""gets all installed apps that are not from django.contrib """gets all installed apps that are not from django.contrib
returns a list of tuples with (path_to_app, app_module) returns a list of tuples with (path_to_app, app_module)
...@@ -56,6 +61,7 @@ def harvest_lettuces(only_the_apps=None, avoid_apps=None, path="features"): ...@@ -56,6 +61,7 @@ def harvest_lettuces(only_the_apps=None, avoid_apps=None, path="features"):
apps = get_apps() apps = get_apps()
if isinstance(only_the_apps, tuple) and any(only_the_apps): if isinstance(only_the_apps, tuple) and any(only_the_apps):
def _filter_only_specified(module): def _filter_only_specified(module):
return module.__name__ in only_the_apps return module.__name__ in only_the_apps
apps = filter(_filter_only_specified, apps) apps = filter(_filter_only_specified, apps)
...@@ -65,6 +71,7 @@ def harvest_lettuces(only_the_apps=None, avoid_apps=None, path="features"): ...@@ -65,6 +71,7 @@ def harvest_lettuces(only_the_apps=None, avoid_apps=None, path="features"):
apps = filter(_filter_configured_avoids, apps) apps = filter(_filter_configured_avoids, apps)
if isinstance(avoid_apps, tuple) and any(avoid_apps): if isinstance(avoid_apps, tuple) and any(avoid_apps):
def _filter_avoid(module): def _filter_avoid(module):
return module.__name__ not in avoid_apps return module.__name__ not in avoid_apps
......
...@@ -28,6 +28,7 @@ from lettuce import registry ...@@ -28,6 +28,7 @@ from lettuce import registry
from lettuce.django import server from lettuce.django import server
from lettuce.django import harvest_lettuces from lettuce.django import harvest_lettuces
class Command(BaseCommand): class Command(BaseCommand):
help = u'Run lettuce tests all along installed apps' help = u'Run lettuce tests all along installed apps'
args = '[PATH to feature file or folder]' args = '[PATH to feature file or folder]'
...@@ -59,6 +60,7 @@ class Command(BaseCommand): ...@@ -59,6 +60,7 @@ class Command(BaseCommand):
make_option('--xunit-file', action='store', dest='xunit_file', default=None, make_option('--xunit-file', action='store', dest='xunit_file', default=None,
help='Write JUnit XML to this file. Defaults to lettucetests.xml'), help='Write JUnit XML to this file. Defaults to lettucetests.xml'),
) )
def stopserver(self, failed=False): def stopserver(self, failed=False):
raise SystemExit(int(failed)) raise SystemExit(int(failed))
...@@ -71,7 +73,7 @@ class Command(BaseCommand): ...@@ -71,7 +73,7 @@ class Command(BaseCommand):
else: else:
paths = args paths = args
else: else:
paths = harvest_lettuces(apps_to_run, apps_to_avoid) # list of tuples with (path, app_module) paths = harvest_lettuces(apps_to_run, apps_to_avoid) # list of tuples with (path, app_module)
return paths return paths
......
...@@ -128,18 +128,12 @@ class ThreadedServer(multiprocessing.Process): ...@@ -128,18 +128,12 @@ class ThreadedServer(multiprocessing.Process):
self.lock.acquire() self.lock.acquire()
def should_serve_static_files(self): def should_serve_static_files(self):
conditions = [ return (StaticFilesHandler is not None and
StaticFilesHandler is not None, getattr(settings, 'STATIC_URL', False))
getattr(settings, 'STATIC_URL', False),
]
return all(conditions)
def should_serve_admin_media(self): def should_serve_admin_media(self):
conditions = [ return ('django.contrib.admin' in settings.INSTALLED_APPS or
'django.contrib.admin' in settings.INSTALLED_APPS, getattr(settings, 'LETTUCE_SERVE_ADMIN_MEDIA', False))
getattr(settings, 'LETTUCE_SERVE_ADMIN_MEDIA', False),
]
return any(conditions)
def run(self): def run(self):
self.lock.acquire() self.lock.acquire()
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import traceback import traceback
class NoDefinitionFound(Exception): class NoDefinitionFound(Exception):
""" Exception raised by lettuce.core.Step, when trying to solve a """ Exception raised by lettuce.core.Step, when trying to solve a
Step, but does not find a suitable step definition. Step, but does not find a suitable step definition.
...@@ -26,8 +27,8 @@ class NoDefinitionFound(Exception): ...@@ -26,8 +27,8 @@ class NoDefinitionFound(Exception):
def __init__(self, step): def __init__(self, step):
self.step = step self.step = step
super(NoDefinitionFound, self).__init__( super(NoDefinitionFound, self).__init__(
'The step r"%s" is not defined' % self.step.sentence 'The step r"%s" is not defined' % self.step.sentence)
)
class ReasonToFail(object): class ReasonToFail(object):
""" Exception that contains detailed information about a """ Exception that contains detailed information about a
...@@ -39,6 +40,7 @@ class ReasonToFail(object): ...@@ -39,6 +40,7 @@ class ReasonToFail(object):
self.cause = unicode(exc) self.cause = unicode(exc)
self.traceback = traceback.format_exc(exc) self.traceback = traceback.format_exc(exc)
class LettuceSyntaxError(SyntaxError): class LettuceSyntaxError(SyntaxError):
def __init__(self, filename, string): def __init__(self, filename, string):
self.filename = filename self.filename = filename
......
...@@ -25,6 +25,7 @@ import zipfile ...@@ -25,6 +25,7 @@ import zipfile
from glob import glob from glob import glob
from os.path import abspath, join, dirname, curdir, exists from os.path import abspath, join, dirname, curdir, exists
class FeatureLoader(object): class FeatureLoader(object):
"""Loader class responsible for findind features and step """Loader class responsible for findind features and step
definitions along a given path on filesystem""" definitions along a given path on filesystem"""
...@@ -47,13 +48,14 @@ class FeatureLoader(object): ...@@ -47,13 +48,14 @@ class FeatureLoader(object):
else: else:
raise e raise e
reload(module) # always take fresh meat :) reload(module) # always take fresh meat :)
sys.path.remove(root) sys.path.remove(root)
def find_feature_files(self): def find_feature_files(self):
paths = FileSystem.locate(self.base_dir, "*.feature") paths = FileSystem.locate(self.base_dir, "*.feature")
return paths return paths
class FileSystem(object): class FileSystem(object):
"""File system abstraction, mainly used for indirection, so that """File system abstraction, mainly used for indirection, so that
lettuce can be well unit-tested :) lettuce can be well unit-tested :)
...@@ -229,4 +231,3 @@ class FileSystem(object): ...@@ -229,4 +231,3 @@ class FileSystem(object):
path = cls.current_dir(name) path = cls.current_dir(name)
return open(path, mode) return open(path, mode)
...@@ -21,12 +21,12 @@ import optparse ...@@ -21,12 +21,12 @@ import optparse
import lettuce import lettuce
def main(args=sys.argv[1:]): def main(args=sys.argv[1:]):
base_path = os.path.join(os.path.dirname(os.curdir), 'features') base_path = os.path.join(os.path.dirname(os.curdir), 'features')
parser = optparse.OptionParser( parser = optparse.OptionParser(
usage="%prog or type %prog -h (--help) for help", usage="%prog or type %prog -h (--help) for help",
version=lettuce.version version=lettuce.version)
)
parser.add_option("-v", "--verbosity", parser.add_option("-v", "--verbosity",
dest="verbosity", dest="verbosity",
...@@ -74,7 +74,7 @@ def main(args=sys.argv[1:]): ...@@ -74,7 +74,7 @@ def main(args=sys.argv[1:]):
verbosity=options.verbosity, verbosity=options.verbosity,
enable_xunit=options.enable_xunit, enable_xunit=options.enable_xunit,
xunit_filename=options.xunit_file, xunit_filename=options.xunit_file,
run_controller = run_controller) run_controller=run_controller)
result = runner.run() result = runner.run()
if not result or result.steps != result.steps_passed: if not result or result.steps != result.steps_passed:
......
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
import os import os
import re import re
import sys import sys
import platform
import struct
from lettuce import core from lettuce import core
from lettuce import strings from lettuce import strings
...@@ -27,12 +25,15 @@ from lettuce import terminal ...@@ -27,12 +25,15 @@ from lettuce import terminal
from lettuce.terrain import after from lettuce.terrain import after
from lettuce.terrain import before from lettuce.terrain import before
def wrt(what): def wrt(what):
sys.stdout.write(what.encode('utf-8')) sys.stdout.write(what.encode('utf-8'))
def wrap_file_and_line(string, start, end): def wrap_file_and_line(string, start, end):
return re.sub(r'([#] [^:]+[:]\d+)', '%s\g<1>%s' % (start, end), string) return re.sub(r'([#] [^:]+[:]\d+)', '%s\g<1>%s' % (start, end), string)
def wp(l): def wp(l):
if l.startswith("\033[1;32m"): if l.startswith("\033[1;32m"):
l = l.replace(" |", "\033[1;37m |\033[1;32m") l = l.replace(" |", "\033[1;37m |\033[1;32m")
...@@ -47,9 +48,11 @@ def wp(l): ...@@ -47,9 +48,11 @@ def wp(l):
return l return l
def write_out(what): def write_out(what):
wrt(wp(what)) wrt(wp(what))
@before.each_step @before.each_step
def print_step_running(step): def print_step_running(step):
if not step.defined_at: if not step.defined_at:
...@@ -67,6 +70,7 @@ def print_step_running(step): ...@@ -67,6 +70,7 @@ def print_step_running(step):
for line in step.represent_hashes().splitlines(): for line in step.represent_hashes().splitlines():
write_out("\033[1;30m%s\033[0m\n" % line) write_out("\033[1;30m%s\033[0m\n" % line)
@after.each_step @after.each_step
def print_step_ran(step): def print_step_ran(step):
if step.scenario.outlines: if step.scenario.outlines:
...@@ -80,7 +84,6 @@ def print_step_ran(step): ...@@ -80,7 +84,6 @@ def print_step_ran(step):
if not step.failed: if not step.failed:
string = wrap_file_and_line(string, '\033[1;30m', '\033[0m') string = wrap_file_and_line(string, '\033[1;30m', '\033[0m')
prefix = '\033[A' prefix = '\033[A'
width, height = terminal.get_size() width, height = terminal.get_size()
lines_up = len(string) / float(width) lines_up = len(string) / float(width)
...@@ -123,12 +126,14 @@ def print_step_ran(step): ...@@ -123,12 +126,14 @@ def print_step_ran(step):
wrt("\033[0m\n") wrt("\033[0m\n")
@before.each_scenario @before.each_scenario
def print_scenario_running(scenario): def print_scenario_running(scenario):
string = scenario.represented() string = scenario.represented()
string = wrap_file_and_line(string, '\033[1;30m', '\033[0m') string = wrap_file_and_line(string, '\033[1;30m', '\033[0m')
write_out("\n\033[1;37m%s" % string) write_out("\n\033[1;37m%s" % string)
@after.outline @after.outline
def print_outline(scenario, order, outline, reasons_to_fail): def print_outline(scenario, order, outline, reasons_to_fail):
table = strings.dicts_to_string(scenario.outlines, scenario.keys) table = strings.dicts_to_string(scenario.outlines, scenario.keys)
...@@ -155,6 +160,7 @@ def print_outline(scenario, order, outline, reasons_to_fail): ...@@ -155,6 +160,7 @@ def print_outline(scenario, order, outline, reasons_to_fail):
wrt("\033[0m\n") wrt("\033[0m\n")
@before.each_feature @before.each_feature
def print_feature_running(feature): def print_feature_running(feature):
string = feature.represented() string = feature.represented()
...@@ -165,6 +171,7 @@ def print_feature_running(feature): ...@@ -165,6 +171,7 @@ def print_feature_running(feature):
line = wrap_file_and_line(line, '\033[1;30m', '\033[0m') line = wrap_file_and_line(line, '\033[1;30m', '\033[0m')
write_out("\033[1;37m%s\n" % line) write_out("\033[1;37m%s\n" % line)
@after.all @after.all
def print_end(total): def print_end(total):
write_out("\n") write_out("\n")
...@@ -179,9 +186,7 @@ def print_end(total): ...@@ -179,9 +186,7 @@ def print_end(total):
total.features_ran, total.features_ran,
word, word,
color, color,
total.features_passed total.features_passed))
)
)
color = "\033[1;32m" color = "\033[1;32m"
if total.scenarios_passed is 0: if total.scenarios_passed is 0:
...@@ -192,9 +197,7 @@ def print_end(total): ...@@ -192,9 +197,7 @@ def print_end(total):
total.scenarios_ran, total.scenarios_ran,
word, word,
color, color,
total.scenarios_passed total.scenarios_passed))
)
)
steps_details = [] steps_details = []
kinds_and_colors = { kinds_and_colors = {
...@@ -203,14 +206,11 @@ def print_end(total): ...@@ -203,14 +206,11 @@ def print_end(total):
'undefined': '\033[0;33m' 'undefined': '\033[0;33m'
} }
for kind, color in kinds_and_colors.items(): for kind, color in kinds_and_colors.items():
attr = 'steps_%s' % kind attr = 'steps_%s' % kind
stotal = getattr(total, attr) stotal = getattr(total, attr)
if stotal: if stotal:
steps_details.append( steps_details.append("%s%d %s" % (color, stotal, kind))
"%s%d %s" % (color, stotal, kind)
)
steps_details.append("\033[1;32m%d passed\033[1;37m" % total.steps_passed) steps_details.append("\033[1;32m%d passed\033[1;37m" % total.steps_passed)
word = total.steps > 1 and "steps" or "step" word = total.steps > 1 and "steps" or "step"
...@@ -220,9 +220,7 @@ def print_end(total): ...@@ -220,9 +220,7 @@ def print_end(total):
write_out("\033[1;37m%d %s (%s)\033[0m\n" % ( write_out("\033[1;37m%d %s (%s)\033[0m\n" % (
total.steps, total.steps,
word, word,
content content))
)
)
if total.proposed_definitions: if total.proposed_definitions:
wrt("\n\033[0;33mYou can implement step definitions for undefined steps with these snippets:\n\n") wrt("\n\033[0;33mYou can implement step definitions for undefined steps with these snippets:\n\n")
...@@ -240,6 +238,7 @@ def print_end(total): ...@@ -240,6 +238,7 @@ def print_end(total):
wrt("\n") wrt("\n")
def print_no_features_found(where): def print_no_features_found(where):
where = core.fs.relpath(where) where = core.fs.relpath(where)
if not where.startswith(os.sep): if not where.startswith(os.sep):
...@@ -248,5 +247,4 @@ def print_no_features_found(where): ...@@ -248,5 +247,4 @@ def print_no_features_found(where):
write_out('\033[1;31mOops!\033[0m\n') write_out('\033[1;31mOops!\033[0m\n')
write_out( write_out(
'\033[1;37mcould not find features at ' '\033[1;37mcould not find features at '
'\033[1;33m%s\033[0m\n' % where '\033[1;33m%s\033[0m\n' % where)
)
...@@ -23,9 +23,11 @@ from lettuce.terrain import after ...@@ -23,9 +23,11 @@ from lettuce.terrain import after
failed_scenarios = [] failed_scenarios = []
scenarios_and_its_fails = {} scenarios_and_its_fails = {}
def wrt(string): def wrt(string):
sys.stdout.write(string) sys.stdout.write(string)
@after.each_step @after.each_step
def print_scenario_ran(step): def print_scenario_ran(step):
if not step.failed: if not step.failed:
...@@ -40,6 +42,7 @@ def print_scenario_ran(step): ...@@ -40,6 +42,7 @@ def print_scenario_ran(step):
else: else:
wrt("E") wrt("E")
@after.all @after.all
def print_end(total): def print_end(total):
if total.scenarios_passed < total.scenarios_ran: if total.scenarios_passed < total.scenarios_ran:
...@@ -54,35 +57,28 @@ def print_end(total): ...@@ -54,35 +57,28 @@ def print_end(total):
wrt("%d %s (%d passed)\n" % ( wrt("%d %s (%d passed)\n" % (
total.features_ran, total.features_ran,
word, word,
total.features_passed total.features_passed))
)
)
word = total.scenarios_ran > 1 and "scenarios" or "scenario" word = total.scenarios_ran > 1 and "scenarios" or "scenario"
wrt("%d %s (%d passed)\n" % ( wrt("%d %s (%d passed)\n" % (
total.scenarios_ran, total.scenarios_ran,
word, word,
total.scenarios_passed total.scenarios_passed))
)
)
steps_details = [] steps_details = []
for kind in ("failed","skipped", "undefined"): for kind in "failed", "skipped", "undefined":
attr = 'steps_%s' % kind attr = 'steps_%s' % kind
stotal = getattr(total, attr) stotal = getattr(total, attr)
if stotal: if stotal:
steps_details.append( steps_details.append("%d %s" % (stotal, kind))
"%d %s" % (stotal, kind)
)
steps_details.append("%d passed" % total.steps_passed) steps_details.append("%d passed" % total.steps_passed)
word = total.steps > 1 and "steps" or "step" word = total.steps > 1 and "steps" or "step"
wrt("%d %s (%s)\n" % ( wrt("%d %s (%s)\n" % (
total.steps, total.steps,
word, word,
", ".join(steps_details) ", ".join(steps_details)))
)
)
def print_no_features_found(where): def print_no_features_found(where):
where = core.fs.relpath(where) where = core.fs.relpath(where)
...@@ -90,7 +86,4 @@ def print_no_features_found(where): ...@@ -90,7 +86,4 @@ def print_no_features_found(where):
where = '.%s%s' % (os.sep, where) where = '.%s%s' % (os.sep, where)
wrt('Oops!\n') wrt('Oops!\n')
wrt( wrt('could not find features at %s\n' % where)
'could not find features at '
'%s\n' % where
)
...@@ -20,24 +20,29 @@ from lettuce import core ...@@ -20,24 +20,29 @@ from lettuce import core
from lettuce.terrain import after from lettuce.terrain import after
from lettuce.terrain import before from lettuce.terrain import before
@before.each_step @before.each_step
def print_step_running(step): def print_step_running(step):
logging.info(step.represent_string(step.sentence)) logging.info(step.represent_string(step.sentence))
@after.each_step @after.each_step
def print_step_ran(step): def print_step_ran(step):
logging.info("\033[A" + step.represent_string(step.sentence)) logging.info("\033[A" + step.represent_string(step.sentence))
@before.each_scenario @before.each_scenario
def print_scenario_running(scenario): def print_scenario_running(scenario):
logging.info(scenario.represented()) logging.info(scenario.represented())
@before.each_feature @before.each_feature
def print_feature_running(feature): def print_feature_running(feature):
logging.info("\n") logging.info("\n")
logging.info(feature.represented()) logging.info(feature.represented())
logging.info("\n") logging.info("\n")
@after.all @after.all
def print_end(total): def print_end(total):
logging.info("\n") logging.info("\n")
...@@ -45,25 +50,20 @@ def print_end(total): ...@@ -45,25 +50,20 @@ def print_end(total):
logging.info("%d %s (%d passed)\n" % ( logging.info("%d %s (%d passed)\n" % (
total.features_ran, total.features_ran,
word, word,
total.features_passed total.features_passed))
)
)
word = total.scenarios_ran > 1 and "scenarios" or "scenario" word = total.scenarios_ran > 1 and "scenarios" or "scenario"
logging.info("%d %s (%d passed)\n" % ( logging.info("%d %s (%d passed)\n" % (
total.scenarios_ran, total.scenarios_ran,
word, word,
total.scenarios_passed total.scenarios_passed))
)
)
word = total.steps > 1 and "steps" or "step" word = total.steps > 1 and "steps" or "step"
logging.info("%d %s (%d passed)\n" % ( logging.info("%d %s (%d passed)\n" % (
total.steps, total.steps,
word, word,
total.steps_passed total.steps_passed))
)
)
def print_no_features_found(where): def print_no_features_found(where):
where = core.fs.relpath(where) where = core.fs.relpath(where)
...@@ -73,6 +73,4 @@ def print_no_features_found(where): ...@@ -73,6 +73,4 @@ def print_no_features_found(where):
logging.info('\033[1;31mOops!\033[0m\n') logging.info('\033[1;31mOops!\033[0m\n')
logging.info( logging.info(
'\033[1;37mcould not find features at ' '\033[1;37mcould not find features at '
'\033[1;33m%s\033[0m\n' % where '\033[1;33m%s\033[0m\n' % where)
)
...@@ -24,13 +24,16 @@ from lettuce.terrain import before ...@@ -24,13 +24,16 @@ from lettuce.terrain import before
failed_scenarios = [] failed_scenarios = []
scenarios_and_its_fails = {} scenarios_and_its_fails = {}
def wrt(string): def wrt(string):
sys.stdout.write(string.encode('utf-8')) sys.stdout.write(string.encode('utf-8'))
@before.each_scenario @before.each_scenario
def print_scenario_running(scenario): def print_scenario_running(scenario):
wrt('%s ... ' % scenario.name) wrt('%s ... ' % scenario.name)
@after.each_scenario @after.each_scenario
def print_scenario_ran(scenario): def print_scenario_ran(scenario):
if scenario.passed: if scenario.passed:
...@@ -42,16 +45,18 @@ def print_scenario_ran(scenario): ...@@ -42,16 +45,18 @@ def print_scenario_ran(scenario):
else: else:
print "ERROR" print "ERROR"
@after.each_step @after.each_step
def save_step_failed(step): def save_step_failed(step):
if step.failed and step.scenario not in failed_scenarios: if step.failed and step.scenario not in failed_scenarios:
scenarios_and_its_fails[step.scenario] = step.why scenarios_and_its_fails[step.scenario] = step.why
failed_scenarios.append(step.scenario) failed_scenarios.append(step.scenario)
@after.all @after.all
def print_end(total): def print_end(total):
if total.scenarios_passed < total.scenarios_ran: if total.scenarios_passed < total.scenarios_ran:
print # just a line to separate things here print # just a line to separate things here
for scenario in failed_scenarios: for scenario in failed_scenarios:
reason = scenarios_and_its_fails[scenario] reason = scenarios_and_its_fails[scenario]
wrt(reason.traceback) wrt(reason.traceback)
...@@ -61,35 +66,25 @@ def print_end(total): ...@@ -61,35 +66,25 @@ def print_end(total):
wrt("%d %s (%d passed)\n" % ( wrt("%d %s (%d passed)\n" % (
total.features_ran, total.features_ran,
word, word,
total.features_passed total.features_passed))
)
)
word = total.scenarios_ran > 1 and "scenarios" or "scenario" word = total.scenarios_ran > 1 and "scenarios" or "scenario"
wrt("%d %s (%d passed)\n" % ( wrt("%d %s (%d passed)\n" % (
total.scenarios_ran, total.scenarios_ran,
word, word,
total.scenarios_passed total.scenarios_passed))
)
)
steps_details = [] steps_details = []
for kind in ("failed","skipped", "undefined"): for kind in "failed", "skipped", "undefined":
attr = 'steps_%s' % kind attr = 'steps_%s' % kind
stotal = getattr(total, attr) stotal = getattr(total, attr)
if stotal: if stotal:
steps_details.append( steps_details.append("%d %s" % (stotal, kind))
"%d %s" % (stotal, kind)
)
steps_details.append("%d passed" % total.steps_passed) steps_details.append("%d passed" % total.steps_passed)
word = total.steps > 1 and "steps" or "step" word = total.steps > 1 and "steps" or "step"
wrt("%d %s (%s)\n" % ( wrt("%d %s (%s)\n" % (total.steps, word, ", ".join(steps_details)))
total.steps,
word,
", ".join(steps_details)
)
)
def print_no_features_found(where): def print_no_features_found(where):
where = core.fs.relpath(where) where = core.fs.relpath(where)
...@@ -97,7 +92,4 @@ def print_no_features_found(where): ...@@ -97,7 +92,4 @@ def print_no_features_found(where):
where = '.%s%s' % (os.sep, where) where = '.%s%s' % (os.sep, where)
wrt('Oops!\n') wrt('Oops!\n')
wrt( wrt('could not find features at %s\n' % where)
'could not find features at '
'%s\n' % where
)
...@@ -22,9 +22,11 @@ from lettuce import strings ...@@ -22,9 +22,11 @@ from lettuce import strings
from lettuce.terrain import after from lettuce.terrain import after
from lettuce.terrain import before from lettuce.terrain import before
def wrt(what): def wrt(what):
sys.stdout.write(what.encode('utf-8')) sys.stdout.write(what.encode('utf-8'))
@after.each_step @after.each_step
def print_step_running(step): def print_step_running(step):
wrt(step.represent_string(step.original_sentence).rstrip()) wrt(step.represent_string(step.original_sentence).rstrip())
...@@ -41,11 +43,13 @@ def print_step_running(step): ...@@ -41,11 +43,13 @@ def print_step_running(step):
for line in step.why.traceback.splitlines(): for line in step.why.traceback.splitlines():
print_spaced(line) print_spaced(line)
@before.each_scenario @before.each_scenario
def print_scenario_running(scenario): def print_scenario_running(scenario):
wrt('\n') wrt('\n')
wrt(scenario.represented()) wrt(scenario.represented())
@after.outline @after.outline
def print_outline(scenario, order, outline, reasons_to_fail): def print_outline(scenario, order, outline, reasons_to_fail):
table = strings.dicts_to_string(scenario.outlines, scenario.keys) table = strings.dicts_to_string(scenario.outlines, scenario.keys)
...@@ -66,11 +70,13 @@ def print_outline(scenario, order, outline, reasons_to_fail): ...@@ -66,11 +70,13 @@ def print_outline(scenario, order, outline, reasons_to_fail):
for line in elines: for line in elines:
print_spaced(line) print_spaced(line)
@before.each_feature @before.each_feature
def print_feature_running(feature): def print_feature_running(feature):
wrt("\n") wrt("\n")
wrt(feature.represented()) wrt(feature.represented())
@after.all @after.all
def print_end(total): def print_end(total):
wrt("\n") wrt("\n")
...@@ -78,35 +84,27 @@ def print_end(total): ...@@ -78,35 +84,27 @@ def print_end(total):
wrt("%d %s (%d passed)\n" % ( wrt("%d %s (%d passed)\n" % (
total.features_ran, total.features_ran,
word, word,
total.features_passed total.features_passed))
)
)
word = total.scenarios_ran > 1 and "scenarios" or "scenario" word = total.scenarios_ran > 1 and "scenarios" or "scenario"
wrt("%d %s (%d passed)\n" % ( wrt("%d %s (%d passed)\n" % (
total.scenarios_ran, total.scenarios_ran,
word, word,
total.scenarios_passed total.scenarios_passed))
)
)
steps_details = [] steps_details = []
for kind in ("failed", "skipped", "undefined"): for kind in ("failed", "skipped", "undefined"):
attr = 'steps_%s' % kind attr = 'steps_%s' % kind
stotal = getattr(total, attr) stotal = getattr(total, attr)
if stotal: if stotal:
steps_details.append( steps_details.append("%d %s" % (stotal, kind))
"%d %s" % (stotal, kind)
)
steps_details.append("%d passed" % total.steps_passed) steps_details.append("%d passed" % total.steps_passed)
word = total.steps > 1 and "steps" or "step" word = total.steps > 1 and "steps" or "step"
wrt("%d %s (%s)\n" % ( wrt("%d %s (%s)\n" % (
total.steps, total.steps,
word, word,
", ".join(steps_details) ", ".join(steps_details)))
)
)
if total.proposed_definitions: if total.proposed_definitions:
wrt("\nYou can implement step definitions for undefined steps with these snippets:\n\n") wrt("\nYou can implement step definitions for undefined steps with these snippets:\n\n")
...@@ -118,14 +116,11 @@ def print_end(total): ...@@ -118,14 +116,11 @@ def print_end(total):
wrt("def %s:\n" % method_name) wrt("def %s:\n" % method_name)
wrt(" assert False, 'This step must be implemented'\n") wrt(" assert False, 'This step must be implemented'\n")
def print_no_features_found(where): def print_no_features_found(where):
where = core.fs.relpath(where) where = core.fs.relpath(where)
if not where.startswith(os.sep): if not where.startswith(os.sep):
where = '.%s%s' % (os.sep, where) where = '.%s%s' % (os.sep, where)
wrt('Oops!\n') wrt('Oops!\n')
wrt( wrt('could not find features at %s\n' % where)
'could not find features at '
'%s\n' % where
)
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from datetime import datetime from datetime import datetime
from lettuce.terrain import after from lettuce.terrain import after
from lettuce.terrain import before from lettuce.terrain import before
...@@ -27,9 +26,11 @@ def wrt_output(filename, content): ...@@ -27,9 +26,11 @@ def wrt_output(filename, content):
f.write(content.encode('utf-8')) f.write(content.encode('utf-8'))
f.close() f.close()
def total_seconds(td): def total_seconds(td):
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 1e6) / 1e6 return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 1e6) / 1e6
def enable(filename=None): def enable(filename=None):
doc = minidom.Document() doc = minidom.Document()
...@@ -51,7 +52,7 @@ def enable(filename=None): ...@@ -51,7 +52,7 @@ def enable(filename=None):
if step.failed: if step.failed:
cdata = doc.createCDATASection(step.why.traceback) cdata = doc.createCDATASection(step.why.traceback)
failure = doc.createElement("failure") failure = doc.createElement("failure")
failure.setAttribute("message",step.why.cause) failure.setAttribute("message", step.why.cause)
failure.appendChild(cdata) failure.appendChild(cdata)
tc.appendChild(failure) tc.appendChild(failure)
...@@ -63,4 +64,3 @@ def enable(filename=None): ...@@ -63,4 +64,3 @@ def enable(filename=None):
root.setAttribute("failed", str(total.steps_failed)) root.setAttribute("failed", str(total.steps_failed))
doc.appendChild(root) doc.appendChild(root)
wrt_output(output_filename, doc.toxml()) wrt_output(output_filename, doc.toxml())
...@@ -22,76 +22,57 @@ world = threading.local() ...@@ -22,76 +22,57 @@ world = threading.local()
world._set = False world._set = False
class CleanableDict(dict): def _function_matches(one, other):
def clear(self): return (one.func_code.co_filename == other.func_code.co_filename and
for k in self.keys(): one.func_code.co_firstlineno == other.func_code.co_firstlineno)
del self[k]
class CallbackDict(CleanableDict):
def _function_matches(self, one, other):
params = 'co_filename', 'co_firstlineno'
matches = list()
for param in params:
one_got = getattr(one.func_code, param)
other_got = getattr(other.func_code, param)
matches.append(one_got == other_got)
return all(matches)
class CallbackDict(dict):
def append_to(self, where, when, function): def append_to(self, where, when, function):
found = False if not any(_function_matches(o, function) for o in self[where][when]):
for other_function in self[where][when]:
if self._function_matches(other_function, function):
found = True
if not found:
self[where][when].append(function) self[where][when].append(function)
def clear(self): def clear(self):
for name, action_dict in self.items(): for name, action_dict in self.items():
for callback_list in action_dict.values(): for callback_list in action_dict.values():
while callback_list: callback_list[:] = []
callback_list.pop()
STEP_REGISTRY = CleanableDict() STEP_REGISTRY = {}
CALLBACK_REGISTRY = CallbackDict( CALLBACK_REGISTRY = CallbackDict(
{ {
'all': { 'all': {
'before': list(), 'before': [],
'after': list(), 'after': [],
}, },
'step': { 'step': {
'before_each': list(), 'before_each': [],
'after_each': list(), 'after_each': [],
}, },
'scenario': { 'scenario': {
'before_each': list(), 'before_each': [],
'after_each': list(), 'after_each': [],
'outline': list(), 'outline': [],
}, },
'feature': { 'feature': {
'before_each': list(), 'before_each': [],
'after_each': list(), 'after_each': [],
}, },
'app': { 'app': {
'before_each': list(), 'before_each': [],
'after_each': list(), 'after_each': [],
}, },
'harvest': { 'harvest': {
'before': list(), 'before': [],
'after': list(), 'after': [],
}, },
'handle_request': { 'handle_request': {
'before': list(), 'before': [],
'after': list(), 'after': [],
}, },
'runserver': { 'runserver': {
'before': list(), 'before': [],
'after': list(), 'after': [],
}, },
}, },
) )
......
...@@ -17,9 +17,9 @@ ...@@ -17,9 +17,9 @@
import re import re
import time import time
import string
import unicodedata import unicodedata
def escape_if_necessary(what): def escape_if_necessary(what):
what = unicode(what) what = unicode(what)
if len(what) is 1: if len(what) is 1:
...@@ -27,6 +27,7 @@ def escape_if_necessary(what): ...@@ -27,6 +27,7 @@ def escape_if_necessary(what):
return what return what
def get_stripped_lines(string, ignore_lines_starting_with=''): def get_stripped_lines(string, ignore_lines_starting_with=''):
string = unicode(string) string = unicode(string)
lines = [unicode(l.strip()) for l in string.splitlines()] lines = [unicode(l.strip()) for l in string.splitlines()]
...@@ -39,12 +40,13 @@ def get_stripped_lines(string, ignore_lines_starting_with=''): ...@@ -39,12 +40,13 @@ def get_stripped_lines(string, ignore_lines_starting_with=''):
return lines return lines
def split_wisely(string, sep, strip=False): def split_wisely(string, sep, strip=False):
string = unicode(string) string = unicode(string)
if strip: if strip:
string=string.strip() string = string.strip()
else: else:
string=string.strip("\n") string = string.strip("\n")
sep = unicode(sep) sep = unicode(sep)
regex = re.compile(escape_if_necessary(sep), re.UNICODE | re.M | re.I) regex = re.compile(escape_if_necessary(sep), re.UNICODE | re.M | re.I)
...@@ -57,15 +59,18 @@ def split_wisely(string, sep, strip=False): ...@@ -57,15 +59,18 @@ def split_wisely(string, sep, strip=False):
return [unicode(i) for i in items] return [unicode(i) for i in items]
def wise_startswith(string, seed): def wise_startswith(string, seed):
string = unicode(string).strip() string = unicode(string).strip()
seed = unicode(seed) seed = unicode(seed)
regex = u"^%s" % re.escape(seed) regex = u"^%s" % re.escape(seed)
return bool(re.search(regex, string, re.I)) return bool(re.search(regex, string, re.I))
def remove_it(string, what): def remove_it(string, what):
return unicode(re.sub(unicode(what), "", unicode(string)).strip()) return unicode(re.sub(unicode(what), "", unicode(string)).strip())
def column_width(string): def column_width(string):
l = 0 l = 0
for c in string: for c in string:
...@@ -75,6 +80,7 @@ def column_width(string): ...@@ -75,6 +80,7 @@ def column_width(string):
l += 1 l += 1
return l return l
def rfill(string, times, char=u" ", append=u""): def rfill(string, times, char=u" ", append=u""):
string = unicode(string) string = unicode(string)
missing = times - column_width(string) missing = times - column_width(string)
...@@ -83,13 +89,17 @@ def rfill(string, times, char=u" ", append=u""): ...@@ -83,13 +89,17 @@ def rfill(string, times, char=u" ", append=u""):
return unicode(string) + unicode(append) return unicode(string) + unicode(append)
def getlen(string): def getlen(string):
return column_width(unicode(string)) + 1 return column_width(unicode(string)) + 1
def dicts_to_string(dicts, order): def dicts_to_string(dicts, order):
escape = "#{%s}" % str(time.time()) escape = "#{%s}" % str(time.time())
def enline(line): def enline(line):
return unicode(line).replace("|", escape) return unicode(line).replace("|", escape)
def deline(line): def deline(line):
return line.replace(escape, '\\|') return line.replace(escape, '\\|')
...@@ -120,12 +130,16 @@ def dicts_to_string(dicts, order): ...@@ -120,12 +130,16 @@ def dicts_to_string(dicts, order):
return deline(u"\n".join(table) + u"\n") return deline(u"\n".join(table) + u"\n")
def parse_hashes(lines): def parse_hashes(lines):
escape = "#{%s}" % str(time.time()) escape = "#{%s}" % str(time.time())
def enline(line): def enline(line):
return unicode(line.replace("\\|", escape)).strip() return unicode(line.replace("\\|", escape)).strip()
def deline(line): def deline(line):
return line.replace(escape, '|') return line.replace(escape, '|')
def discard_comments(lines): def discard_comments(lines):
return [line for line in lines if not line.startswith('#')] return [line for line in lines if not line.startswith('#')]
...@@ -146,6 +160,7 @@ def parse_hashes(lines): ...@@ -146,6 +160,7 @@ def parse_hashes(lines):
return keys, hashes return keys, hashes
def parse_multiline(lines): def parse_multiline(lines):
multilines = [] multilines = []
in_multiline = False in_multiline = False
...@@ -163,11 +178,11 @@ def parse_multiline(lines): ...@@ -163,11 +178,11 @@ def parse_multiline(lines):
def extract_tags_from_line(given_line): def extract_tags_from_line(given_line):
"""returns tags_array if given_line contains tags, else None""" """returns tags_array if given_line contains tags, else None"""
line = string.rstrip(given_line) line = given_line.rstrip()
tags = [] tags = []
if re.match("\s*?\@", line): if re.match("\s*?\@", line):
tags = [tag for tag in re.split("\s*\@", line) if len(tag) > 0] tags = [tag for tag in re.split("\s*\@", line) if len(tag) > 0]
if len(tags) == 0 or [tag for tag in tags if string.find(tag, " ") != -1]: if len(tags) == 0 or any(' ' in tag for tag in tags):
return None return None
return tags return tags
...@@ -185,6 +200,7 @@ def consume_tags_lines(lines, tags): ...@@ -185,6 +200,7 @@ def consume_tags_lines(lines, tags):
else: else:
break break
def consume_scenario(lines, scenario_prefix): def consume_scenario(lines, scenario_prefix):
"""return string of scenario text """return string of scenario text
and reduce lines array by that much""" and reduce lines array by that much"""
...@@ -205,6 +221,7 @@ def consume_scenario(lines, scenario_prefix): ...@@ -205,6 +221,7 @@ def consume_scenario(lines, scenario_prefix):
scenario_lines.extend(get_lines_till_next_scenario(lines, scenario_prefix)) scenario_lines.extend(get_lines_till_next_scenario(lines, scenario_prefix))
return unicode("\n".join(scenario_lines)) return unicode("\n".join(scenario_lines))
def get_lines_till_next_scenario(lines, scenario_prefix): def get_lines_till_next_scenario(lines, scenario_prefix):
"""returns array of lines up till next scenario block""" """returns array of lines up till next scenario block"""
sep = unicode(scenario_prefix) sep = unicode(scenario_prefix)
...@@ -222,6 +239,7 @@ def get_lines_till_next_scenario(lines, scenario_prefix): ...@@ -222,6 +239,7 @@ def get_lines_till_next_scenario(lines, scenario_prefix):
scenario_lines.append(lines.pop(0)) scenario_lines.append(lines.pop(0))
return scenario_lines return scenario_lines
def split_scenarios(lines, scenario_prefix): def split_scenarios(lines, scenario_prefix):
"""returns array of strings, one per scenario""" """returns array of strings, one per scenario"""
scenario_strings = [] scenario_strings = []
......
...@@ -18,6 +18,7 @@ import os ...@@ -18,6 +18,7 @@ import os
import platform import platform
import struct import struct
def get_size(): def get_size():
if platform.system() == "Windows": if platform.system() == "Windows":
size = get_terminal_size_win() size = get_terminal_size_win()
...@@ -29,6 +30,7 @@ def get_size(): ...@@ -29,6 +30,7 @@ def get_size():
return size return size
def get_terminal_size_win(): def get_terminal_size_win():
#Windows specific imports #Windows specific imports
from ctypes import windll, create_string_buffer from ctypes import windll, create_string_buffer
...@@ -41,20 +43,21 @@ def get_terminal_size_win(): ...@@ -41,20 +43,21 @@ def get_terminal_size_win():
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
if res: if res:
import struct (bufx, bufy, curx, cury, wattr, left, top, right, bottom,
(bufx, bufy, curx, cury, wattr, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
sizex = right - left + 1 sizex = right - left + 1
sizey = bottom - top + 1 sizey = bottom - top + 1
else: else: # can't determine actual size - return default values
sizex, sizey = 80, 25 # can't determine actual size - return default values sizex, sizey = 80, 25
return sizex, sizey return sizex, sizey
def get_terminal_size_unix(): def get_terminal_size_unix():
# Unix/Posix specific imports # Unix/Posix specific imports
import fcntl, termios import fcntl
import termios
def ioctl_GWINSZ(fd): def ioctl_GWINSZ(fd):
try: try:
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
......
...@@ -18,6 +18,7 @@ from lettuce.registry import world ...@@ -18,6 +18,7 @@ from lettuce.registry import world
from lettuce.registry import CALLBACK_REGISTRY from lettuce.registry import CALLBACK_REGISTRY
world._set = True world._set = True
def absorb(thing, name=None): def absorb(thing, name=None):
if not isinstance(name, basestring): if not isinstance(name, basestring):
name = thing.__name__ name = thing.__name__
...@@ -27,6 +28,7 @@ def absorb(thing, name=None): ...@@ -27,6 +28,7 @@ def absorb(thing, name=None):
world.absorb = absorb world.absorb = absorb
@world.absorb @world.absorb
def spew(name): def spew(name):
if hasattr(world, name): if hasattr(world, name):
...@@ -34,54 +36,29 @@ def spew(name): ...@@ -34,54 +36,29 @@ def spew(name):
delattr(world, name) delattr(world, name)
return item return item
class main(object):
@classmethod
def all(cls, function):
CALLBACK_REGISTRY.append_to('all', cls.__name__, function)
return function
@classmethod
def each_step(cls, function):
CALLBACK_REGISTRY.append_to('step', "%s_each" % cls.__name__, function)
return function
@classmethod
def each_scenario(cls, function):
CALLBACK_REGISTRY.append_to('scenario', "%s_each" % cls.__name__, function)
return function
@classmethod
def each_feature(cls, function):
CALLBACK_REGISTRY.append_to('feature', "%s_each" % cls.__name__, function)
return function
@classmethod
def harvest(cls, function):
CALLBACK_REGISTRY.append_to('harvest', cls.__name__, function)
return function
@classmethod
def each_app(cls, function):
CALLBACK_REGISTRY.append_to('app', "%s_each" % cls.__name__, function)
return function
@classmethod
def runserver(cls, function):
CALLBACK_REGISTRY.append_to('runserver', cls.__name__, function)
return function
@classmethod class Main(object):
def handle_request(cls, function): def __init__(self, callback):
CALLBACK_REGISTRY.append_to('handle_request', cls.__name__, function) self.name = callback
return function
@classmethod @classmethod
def outline(cls, function): def _add_method(cls, name, where, when):
CALLBACK_REGISTRY.append_to('scenario', "outline", function) def method(self, fn):
return function CALLBACK_REGISTRY.append_to(where, when.format(self.name), fn)
method.__name__ = method.fn_name = name
setattr(cls, name, method)
class before(main): for name, where, when in (
pass ('all', 'all', '{0}'),
('each_step', 'step', '{0}_each'),
('each_scenario', 'scenario', '{0}_each'),
('each_feature', 'feature', '{0}_each'),
('harvest', 'harvest', '{0}'),
('each_app', 'app', '{0}_each'),
('runserver', 'runserver', '{0}'),
('handle_request', 'handle_request', '{0}'),
('outline', 'scenario', 'outline')):
Main._add_method(name, where, when)
class after(main): before = Main('before')
pass after = Main('after')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment