Skip to content

Commit

Permalink
huge bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielfalcao committed Apr 20, 2010
1 parent e30d613 commit 8d1afaf
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 24 deletions.
10 changes: 8 additions & 2 deletions lettuce/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
version = '0.1'
release = 'barium'


import sys

from lettuce import fs
from lettuce.core import Feature

Expand All @@ -30,6 +30,8 @@
from lettuce.decorators import step
from lettuce.registry import CALLBACK_REGISTRY

from couleur import Shell

__all__ = ['after', 'before', 'step', 'world']

def _import(name):
Expand All @@ -41,14 +43,15 @@ class Runner(object):
Takes a base path as parameter (string), so that it can look for
features and step definitions on there.
"""
def __init__(self, base_path):
def __init__(self, base_path, verbosity=4):
""" lettuce.Runner will try to find a terrain.py file and
import it from within `base_path`
"""
sys.path.insert(0, base_path)
fs.FileSystem.pushd(base_path)
self.terrain = None
self.loader = fs.FeatureLoader(base_path)
self.verbosity = verbosity
try:
self.terrain = _import("terrain")
except ImportError, e:
Expand All @@ -58,6 +61,9 @@ def __init__(self, base_path):
sys.path.remove(base_path)
fs.FileSystem.popd()

world._runner = self
world._shell = Shell(linebreak=True, disabled=verbosity is not 4)

def run(self):
""" Find and load step definitions, and them find and load
features under `base_path` specified on constructor
Expand Down
40 changes: 31 additions & 9 deletions lettuce/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from lettuce import strings
from lettuce.registry import STEP_REGISTRY
from lettuce.registry import CALLBACK_REGISTRY
from lettuce.registry import world
from lettuce.exceptions import ReasonToFail
from lettuce.exceptions import NoDefinitionFound

Expand Down Expand Up @@ -74,13 +75,23 @@ class FeatureDescription(object):
"""A simple object that holds filename and line number of a feature
description"""

def __init__(self, filename, lines):
def __init__(self, feature, filename, string):
lines = [l.strip() for l in string.splitlines()]
self.file = filename
self.line = None
described_at = []
description_lines = strings.get_stripped_lines(feature.description)
for pline, part in enumerate(lines):
if part.strip().startswith("Feature:"):
self.line = pline + 2
break
part = part.strip()
line = pline + 1
if part.startswith("Feature:"):
self.line = line
else:
for description in description_lines:
if part == description:
described_at.append(line)

self.description_at = tuple(described_at)

class Step(object):
""" Object that represents each step on feature files."""
Expand Down Expand Up @@ -355,7 +366,9 @@ def __init__(self, name, remaining_lines, with_file, original_string):
self.original_string = original_string

if with_file:
feature_definition = FeatureDescription(with_file, remaining_lines)
feature_definition = FeatureDescription(self,
with_file,
original_string)
self._set_definition(feature_definition)

self._add_myself_to_scenarios()
Expand All @@ -381,13 +394,22 @@ def _add_myself_to_scenarios(self):
def __repr__(self):
return u'<Feature: "%s">' % self.name

def represented(self, color=True):
return ''

@classmethod
def from_string(new_feature, string, with_file=None):
"""Creates a new feature from string"""
lines = strings.get_stripped_lines(string)
feature_line = lines.pop(0)
line = feature_line.replace("Feature: ", "")
feature = new_feature(name=line,
while lines:
matched = re.search(r'Feature:(.*)', lines[0], re.I)
if matched:
name = matched.groups()[0].strip()
break

lines.pop(0)

feature = new_feature(name=name,
remaining_lines=lines,
with_file=with_file,
original_string=string)
Expand All @@ -406,7 +428,7 @@ def _set_definition(self, definition):
self.described_at = definition

def _parse_remaining_lines(self, lines, original_string, with_file=None):
joined = "\n".join(lines)
joined = "\n".join(lines[1:])
regex = re.compile("Scenario( Outline)?[:]\s", re.I)
joined = regex.sub('Scenario: ', joined)
parts = strings.split_wisely(joined, "Scenario[:] ")
Expand Down
7 changes: 7 additions & 0 deletions lettuce/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ def wise_startswith(string, seed):

def remove_it(string, what):
return re.sub(what, "", string).strip()

def rfill(string, times, char=" ", append=""):
missing = times - len(string)
for x in range(missing):
string += char

return string + append
3 changes: 3 additions & 0 deletions tests/functional/1st_feature_dir/some.feature
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# language: en
# some comment here


Feature: Addition
In order to avoid silly mistakes
As a math idiot
Expand Down
7 changes: 2 additions & 5 deletions tests/functional/runner_features/dumb_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
from lettuce import step

@step('Given I do nothing')
def do_nothing(step):
pass

def do_nothing(step): pass
@step('Then I see that the test passes')
def see_test_passes(step):
pass
def see_test_passes(step): pass
2 changes: 1 addition & 1 deletion tests/functional/runner_features/first.feature
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Feature: Dumb feature
In order to test success
As a programmer
I want to see that the output is green as lettuce leaves
I want to see that the output is green

Scenario: Do nothing
Given I do nothing
Expand Down
19 changes: 19 additions & 0 deletions tests/functional/test_feature_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ def test_feature_loaded_from_file_has_feature_line_and_feature_filename():
feature = Feature.from_file(feature_file)
assert_equals(feature.described_at.file, feature_file)
assert_equals(feature.described_at.line, 2)
assert_equals(feature.name, 'Division')
assert_equals(feature.described_at.description_at, (3, 4))

def test_feature_loaded_from_file_has_description_at():
"Feature.from_file sets FeatureDescription with line numbers of its description"

feature_file = cjoin('1st_feature_dir', 'some.feature')

feature = Feature.from_file(feature_file)
assert_equals(feature.described_at.file, feature_file)
assert_equals(feature.described_at.line, 5)
assert_equals(feature.name, 'Addition')
assert_equals(feature.described_at.description_at, (6, 7, 8))
assert_equals(
feature.description,
"In order to avoid silly mistakes\n"
"As a math idiot\n"
"I want to be told the sum of two numbers"
)

def test_feature_loaded_from_file_sets_scenario_line_and_scenario_filename():
"Feature.from_file sets ScenarioDescription into Scenario objects, " \
Expand Down
94 changes: 90 additions & 4 deletions tests/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,100 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from StringIO import StringIO

from os.path import dirname, abspath, join
from nose.tools import assert_equals
from nose.tools import assert_equals, with_setup

from lettuce import Runner
from lettuce.terrain import before, world, after
from lettuce.fs import FeatureLoader
from lettuce.core import Feature
from lettuce.core import Scenario
from lettuce.core import Step

current_dir = abspath(dirname(__file__))
cjoin = lambda *x: join(current_dir, *x)

def prepare_stdout():
if isinstance(sys.stdout, StringIO):
del sys.stdout

std = StringIO()
sys.stdout = std

def assert_stdout(expected):
string = sys.stdout.getvalue()
assert_equals(string, expected)

def _test_feature_representation_without_colors():
"Feature represented without colors"
feature_file = cjoin('1st_feature_dir', 'some.feature')

feature = Feature.from_file(feature_file)
assert_equals(
feature.represented(color=False),
"Feature: Adition # 1st_feature_dir/some.feature:5\n"
" In order to avoid silly mistakes # 1st_feature_dir/some.feature:6\n"
" As a math idiot # 1st_feature_dir/some.feature:7\n"
" I want to be told the sum of two numbers # 1st_feature_dir/some.feature:8\n"
)

def _test_scenario_representation_without_colors():
"Scenario represented without colors"
feature_file = cjoin('1st_feature_dir', 'some.feature')

def test_output_with_success():
feature = Feature.from_file(feature_file)
assert_equals(
feature.scenarios[0].represented(color=False),
" Scenario Outline: Add two numbers # 1st_feature_dir/some.feature:10\n"
)

def _test_undefined_step_representation_without_colors():
"Undefined step represented without colors"
feature_file = cjoin('runner_features', 'first.feature')

feature = Feature.from_file(feature_file)
assert_equals(
feature.scenarios[0].steps[0].represented(color=False),
" Given I do nothing # runner_features/first.feature:7\n"
)
assert_equals(
feature.scenarios[0].steps[1].represented(color=False),
" Then I see that the test passes # runner_features/first.feature:8\n"
)

def _test_defined_step_representation_without_colors():
"Defined step represented without colors"
feature_file = cjoin('runner_features', 'first.feature')
loader = FeatureLoader('runner_features')
loader.find_and_load_step_definitions()

feature = Feature.from_file(feature_file)

assert_equals(
feature.scenarios[0].solved_steps[0].represented(color=False),
" Given I do nothing # runner_features/dumb_steps.py:6\n"
)
assert_equals(
feature.scenarios[0].solved_steps[1].represented(color=False),
" Then I see that the test passes # runner_features/dumb_steps.py:7\n"
)

@with_setup(prepare_stdout)
def _test_output_with_success_colorless():
"Testing the output of a successful feature"

runner = Runner(join(abspath(dirname(__file__)), 'runner_features'))
runner = Runner(join(abspath(dirname(__file__)), 'runner_features'), verbosity=3)
runner.run()

assert_stdout(
"Feature: Dumb feature # runner_features/first.feature: 1\n"
" In order to test success\n"
" As a programmer\n"
" I want to see that the output is green\n"
"\n"
" Scenario: Do nothing # runner_features/first.feature: 6\n"
" Given I do nothing # runner_features/dumb_steps.py: 6\n"
" Then I see that the test passes # runner_features/dumb_steps.py: 8\n"
)
4 changes: 2 additions & 2 deletions tests/functional/test_step_definition_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_loads_sum_steps():
def assert_is_fine(step):
world.ran = True

runner = Runner(join(abspath(dirname(__file__)), '2nd_feature_dir'))
runner = Runner(join(abspath(dirname(__file__)), '2nd_feature_dir'), verbosity=0)
runner.run()

assert world.ran
Expand All @@ -40,7 +40,7 @@ def test_recursive_fallback():

world.step_list = list()

runner = Runner(join(abspath(dirname(__file__)), '3rd_feature_dir'))
runner = Runner(join(abspath(dirname(__file__)), '3rd_feature_dir'), verbosity=0)
runner.run()

assert_equals(
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_scenario_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,3 @@ def test_full_featured_feature():

for step, expected_sentence in zip(scenario4.solved_steps, expected_sentences):
assert_equals(step.sentence, expected_sentence)

35 changes: 35 additions & 0 deletions tests/unit/test_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,38 @@ def test_remove_it_accepts_regex_to_remove_all_from_string():
strings.remove_it("Gabriel Falcão", "[aã]"),
"Gbriel Flco"
)

def test_rfill_simple():
"strings.rfill simple case"
assert_equals(
strings.rfill("ab", 10, "-"),
"ab--------"
)

def test_rfill_empty():
"strings.rfill empty"
assert_equals(
strings.rfill("", 10, "*"),
"**********"
)

def test_rfill_blank():
"strings.rfill blank"
assert_equals(
strings.rfill(" ", 10, "|"),
" |||||||||"
)

def test_rfill_full():
"strings.rfill full"
assert_equals(
strings.rfill("abcdefghij", 10, "|"),
"abcdefghij"
)

def test_rfill_append():
"strings.rfill append"
assert_equals(
strings.rfill("ab", 10, append="# path/to/file.extension: 2"),
"ab # path/to/file.extension: 2"
)

0 comments on commit 8d1afaf

Please sign in to comment.