Skip to content

Commit

Permalink
Merge pull request #40 from AllYarnsAreBeautiful/test-default-instruc…
Browse files Browse the repository at this point in the history
…tions

Test default instructions
  • Loading branch information
kirstin committed Jul 6, 2016
2 parents b636364 + 6edc837 commit 4cd8747
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 44 deletions.
44 changes: 43 additions & 1 deletion knittingpattern/Instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,43 @@
# pattern specification

ID = "id" #: the id key in the specification

TYPE = "type" #: the type key in the specification

KNIT_TYPE = "knit" #: the type of the knit instruction

PURL_TYPE = "purl" #: the type of the purl instruction

#: the type of the instruction without a specified type
DEFAULT_TYPE = KNIT_TYPE

COLOR = "color" #: the color key in the specification

DESCRIPTION = "description" #: the description in the specification

#: the key for the number of meshes that a instruction consumes
NUMBER_OF_CONSUMED_MESHES = "number of consumed meshes"

#: the default number of meshes that a instruction consumes
DEFAULT_NUMBER_OF_CONSUMED_MESHES = 1

#: the key for the number of meshes that a instruction produces
NUMBER_OF_PRODUCED_MESHES = "number of produced meshes"

#: the default number of meshes that a instruction produces
DEFAULT_NUMBER_OF_PRODUCED_MESHES = 1

#: The default z-index, see :func:`get_z`.
DEFAULT_Z = 0

#: Instructions have a default specification. In this specification the key
#: in :data:`RENDER` points to configuration for rendering.
RENDER = "render"

#: The key to look for the z-index inside the :data:`render` specification.
#: .. seealso:: :func:`get_z`, :data:`DEFAULT_Z`
RENDER_Z = "z"

# error messages

INSTRUCTION_NOT_FOUND_MESSAGE = \
Expand Down Expand Up @@ -86,6 +108,15 @@ def color(self):
"""
return self.get(COLOR)

@property
def description(self):
"""The description of the instruction.
:return: the :data:`description <DESCRIPTION>` of the instruction or
:obj:`None` if none is specified.
"""
return self.get(DESCRIPTION)

@property
def number_of_consumed_meshes(self):
"""The number of meshes that this inctrucion consumes.
Expand Down Expand Up @@ -152,6 +183,16 @@ def consumes_meshes(self):
"""
return self.number_of_consumed_meshes != 0

@property
def render_z(self):
"""The z-index of the instruction when rendered.
:return: the z-index of the instruction. Instructions with a higher
z-index are displayed in front of instructions with lower z-index.
:rtype: float
"""
return self.get(RENDER, {}).get(RENDER_Z, DEFAULT_Z)

@property
def hex_color(self):
"""The color in "#RRGGBB" format.
Expand Down Expand Up @@ -513,4 +554,5 @@ class InstructionNotFoundInRow(ValueError):
__all__ = ["Instruction", "InstructionInRow", "InstructionNotFoundInRow",
"ID", "TYPE", "KNIT_TYPE", "PURL_TYPE", "DEFAULT_TYPE", "COLOR",
"NUMBER_OF_CONSUMED_MESHES", "DEFAULT_NUMBER_OF_CONSUMED_MESHES",
"NUMBER_OF_PRODUCED_MESHES", "DEFAULT_NUMBER_OF_PRODUCED_MESHES"]
"NUMBER_OF_PRODUCED_MESHES", "DEFAULT_NUMBER_OF_PRODUCED_MESHES",
"RENDER_Z", "RENDER", "DEFAULT_Z"]
9 changes: 9 additions & 0 deletions knittingpattern/InstructionLibrary.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ def __getitem__(self, instruction_type):
"""
return self.as_instruction({TYPE: instruction_type})

@property
def loaded_types(self):
"""The types loaded in this library.
:return: a list of types, preferably as :class:`string <str>`
:rtype: list
"""
return list(self._type_to_instruction)


class DefaultInstructions(InstructionLibrary):
"""The default specifications for instructions ported with this package
Expand Down
2 changes: 1 addition & 1 deletion knittingpattern/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""
# there should be no imports

__version__ = '0.1.2'
__version__ = '0.1.3'


def load_from():
Expand Down
32 changes: 4 additions & 28 deletions knittingpattern/convert/KnittingPatternToSVG.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,6 @@
#: The svg tag is renamed to the tag given in :data:`DEFINITION_HOLDER`.
DEFINITION_HOLDER = "g"

#: The default z-index, see :func:`get_z`.
DEFAULT_Z = 0

#: Instructions have a default specification. In this specification the key
#: in :data:`RENDER` points to configuration for rendering.
RENDER = "render"

#: The key to look for the z-index inside the :data:`render` specification.
#: .. seealso:: :func:`get_z`, :data:`DEFAULT_Z`
RENDER_Z = "z"


def get_z(instruction):
"""The z-index of the pattern.
:param knittingpattern.Instruction.Instruction instruction:
the instruction to compute the z-index from
:return: the z-index of the instruction. Instructions with a higher z-index
are displayed in front of instructions with lower z-index.
:rtype: float
"""
return instruction.get(RENDER, {}).get(RENDER_Z, DEFAULT_Z)


class KnittingPatternToSVG(object):
"""Converts a KnittingPattern to SVG.
Expand Down Expand Up @@ -75,10 +52,10 @@ def build_SVG_dict(self):
lambda i: (flip_x - (i.x + i.width) * zoom,
flip_y - (i.y + i.height) * zoom,
i.instruction)))
instructions.sort(key=lambda x_y_i: get_z(x_y_i[2]))
instructions.sort(key=lambda x_y_i: x_y_i[2].render_z)
for x, y, instruction in instructions:
render_z = get_z(instruction)
z_id = ("" if render_z == DEFAULT_Z else "-{}".format(render_z))
render_z = instruction.render_z
z_id = ("" if not render_z else "-{}".format(render_z))
layer_id = "row-{}{}".format(instruction.row.id, z_id)
def_id = self._register_instruction_in_defs(instruction)
scale = self._symbol_id_to_scale[def_id]
Expand Down Expand Up @@ -151,5 +128,4 @@ def _compute_scale(self, instruction_id, svg_dict):
scale = self._zoom / (bbox[3] - bbox[1])
self._symbol_id_to_scale[instruction_id] = scale

__all__ = ["KnittingPatternToSVG", "get_z", "DEFINITION_HOLDER", "RENDER_Z",
"RENDER", "DEFAULT_Z"]
__all__ = ["KnittingPatternToSVG", "DEFINITION_HOLDER"]
17 changes: 17 additions & 0 deletions knittingpattern/convert/test/test_default_instruction_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Test rendering propetries of the default instructions."""
from test_convert import fixture
from knittingpattern.InstructionLibrary import default_instructions


@fixture
def default():
return default_instructions()


def test_knit_has_no_z_index(default):
assert default["knit"].render_z == 0


def test_yo_has_z_index(default):
assert default["yo"].render_z == 1
assert default["yo twisted"].render_z == 1
8 changes: 8 additions & 0 deletions knittingpattern/instructions/cdd.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
"number of consumed meshes" : 3,
"title" : {
"en-en" : "Center Double Decrease"
},
"description" : {
"text" : {
"en-en" : "Knit tree meshes together in the middle."
},
"youtube" : {
"en-en" : "https://www.youtube.com/watch?v=Wi5JpkOoLCI"
}
}
}
]
44 changes: 30 additions & 14 deletions knittingpattern/test/test_default_instructions.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,51 @@
from test_knittingpattern import fixture
from test_knittingpattern import fixture, pytest
from knittingpattern.InstructionLibrary import DefaultInstructions, \
default_instructions


DEFAULT_INSTRUCTIONS = {
"knit": (1, 1),
"purl": (1, 1),
"skp": (2, 1),
"yo": (0, 1),
"yo twisted": (0, 1),
"k2tog": (2, 1),
"bo": (1, 0),
"cdd": (3, 1),
"co": (0, 1)
}


@fixture
def default():
return DefaultInstructions()


def test_default_instructions_is_a_singleton():
assert default_instructions() is default_instructions()
@pytest.mark.parametrize("type_,value", DEFAULT_INSTRUCTIONS.items())
def test_mesh_consumption(default, type_, value):
assert default[type_].number_of_consumed_meshes == value[0]


def test_knitting_instruction(default):
assert default["knit"].type == "knit"
@pytest.mark.parametrize("type_,value", DEFAULT_INSTRUCTIONS.items())
def test_mesh_production(default, type_, value):
assert default[type_].number_of_produced_meshes == value[1]


def test_purl_instruction(default):
assert default["skp"]["number of consumed meshes"] == 2
@pytest.mark.parametrize("type_", DEFAULT_INSTRUCTIONS.keys())
def test_description_present(default, type_):
assert default[type_].description

UNTEDTED_MESSAGE = "No default instructions shall be untested."

def test_yarn_over(default):
assert default["yo"]["number of consumed meshes"] == 0

def test_all_default_instructions_are_tested(default):
untested_instructions = \
set(default.loaded_types) - set(DEFAULT_INSTRUCTIONS)
assert not untested_instructions, UNTEDTED_MESSAGE

def test_bind_off(default):
assert default["bo"]["number of produced meshes"] == 0


def test_cast_on(default):
assert default["co"]["number of consumed meshes"] == 0
def test_default_instructions_is_a_singleton():
assert default_instructions() is default_instructions()


def test_default_instructions_are_an_instance_of_the_class():
Expand Down
11 changes: 11 additions & 0 deletions knittingpattern/test/test_instruction_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,14 @@ def test_library_handles_loading_several_instructions_with_same_type(library2):

def test_access_via_type(library):
assert library["knit"]["type"] == "knit"

UNLOADED = "unloaded type"


def test_when_library_load_instruction_it_is_in_its_types(library2):
library2.add_instruction({"type": UNLOADED})
assert UNLOADED in library2.loaded_types


def test_unloaded_instruction_is_not_in_the_types(library2):
assert UNLOADED not in library2.loaded_types

0 comments on commit 4cd8747

Please sign in to comment.