Source code for unit.canvas.svg

# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Timeline is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Timeline.  If not, see <http://www.gnu.org/licenses/>.


from unittest.mock import Mock

from timelinelib.canvas.svg import SVGDrawingAlgorithm, G
from timelinelib.canvas.drawing.scene import TimelineScene
from timelinelib.canvas.drawing.viewproperties import ViewProperties
from timelinelib.canvas.data.event import Event
from timelinelib.canvas.data.era import Era
from timelinelib.test.cases.unit import UnitTestCase


[docs]class describe_svg_drawing_algorithm(UnitTestCase): maxDiff = None
[docs] def test_can_draw_divider_line(self): shape = self.svg._draw_divider_line() self.assertSvgEqual(shape.getXML(), '<line y1="200" x2="400" style="stroke:grey; stroke-width:0.5; " x1="0" y2="200" />\n')
[docs] def test_can_draw_now_line(self): shape = self.svg._draw_now_line() self.assertSvgEqual(shape.getXML(), '<line y1="0" x2="150" style="stroke:darkred; stroke-width:0.5; " x1="150" y2="200" />\n')
[docs] def test_can_draw_line_to_selected_non_period_events(self): self.view_properties.is_selected.return_value = True self.scene.event_data = ((self.point_event, self.point_event_rect),) group = G() self.svg._draw_lines_to_non_period_events(group, self.view_properties) self.assertSvgEqual(group.getXML(), '<g >\n<line y1="106" x2="200" style="stroke:red; stroke-width:1; " x1="200" y2="200" />\n<circle cy="200" cx="200" r="2" style="stroke:black; stroke-width:1; fill:none; " />\n</g>\n')
[docs] def test_can_draw_major_strip_divider_line(self): time = Mock() self.scene.x_pos_for_time.return_value = 170 line = self.svg._draw_major_strip_divider_line(time) self.assertSvgEqual(line.getXML(), '<line y1="0" x2="170" style="stroke:black; stroke-width:0.5; " x1="170" y2="200" />\n')
[docs] def test_can_draw_minor_strip_divider_line(self): time = Mock() self.scene.x_pos_for_time.return_value = 170 line = self.svg._draw_minor_strip_divider_line(time) self.assertSvgEqual(line.getXML(), '<line y1="0" x2="170" style="stroke:lightgrey; stroke-width:0.5; " x1="170" y2="200" />\n')
[docs] def test_can_draw_line_to_nonselected_non_period_events(self): self.scene.event_data = ((self.point_event, self.point_event_rect),) group = G() self.svg._draw_lines_to_non_period_events(group, self.view_properties) self.assertSvgEqual(group.getXML(), '<g >\n<line y1="106" x2="200" style="stroke:black; stroke-width:1; " x1="200" y2="200" />\n<circle cy="200" cx="200" r="2" style="stroke:black; stroke-width:1; fill:none; " />\n</g>\n')
[docs] def test_can_draw_minor_strip_label(self): strip = Mock() strip.label.return_value = "Label" strip_period = Mock() self.scene.x_pos_for_time.return_value = 100 self.scene.minor_strip = strip text = self.svg._draw_minor_strip_label(strip_period) self.assertSvgEqual(text.getXML(), '<text style="font-size:9px; font-family:Verdana; stroke-dasharray:(2, 2); text-anchor:left; " y="195" x="91" >\nLabel</text>\n')
[docs] def test_can_draw_major_strip_label(self): strip = Mock() strip.label.return_value = "2016" strip_period = Mock() self.scene.x_pos_for_time.return_value = 100 self.scene.major_strip = strip text = self.svg._draw_major_strip_label(strip_period) self.assertSvgEqual(text.getXML(), '<text style="font-size:14px; font-family:Verdana; text-anchor:left; " y="19" x="100" >\n2016</text>\n')
[docs] def test_now_line_is_visible(self): self.scene.x_pos_for_now.return_value = 100 self.assertTrue(self.svg._now_line_is_visible())
[docs] def test_now_line_is_not_visible(self): self.scene.x_pos_for_now.return_value = 2000 self.assertFalse(self.svg._now_line_is_visible()) self.scene.x_pos_for_now.return_value = -100 self.assertFalse(self.svg._now_line_is_visible())
[docs] def test_can_draw_event(self): event = Mock(Event) event.has_data.return_value = True event.text = "Foo" event.category = None event.get_default_color.return_value = (200, 200, 200) rect = Mock() rect.X = 10 rect.Y = 20 rect.Width = 50 rect.GetWidth.return_value = 50 rect.GetHeight.return_value = 8 rect.Get.return_value = (10, 20, 50, 8) group = self.svg._draw_event(event, rect) self.assertSvgEqual(group.getXML(), '<g >\n<rect style="stroke:#8C8C8C; stroke-width:1; fill:#C8C8C8; " height="8" width="50" y="20" x="10" />\n<g clip-path="url(#path10_20_50)" >\n<text style="font-size:9px; font-family:Verdana; stroke-dasharray:(2, 2); text-anchor:left; " y="25" x="13" lengthAdjust="spacingAndGlyphs" >\nFoo</text>\n</g>\n<polygon style="stroke:#787878; stroke-width:1; fill:#787878; " points="50,20 60,20 60,30" />\n</g>\n')
[docs] def test_can_draw_event_with_centered_text(self): self.scene.center_text.return_value = True event = Mock(Event) event.has_data.return_value = True event.text = "Foo" event.category = None event.get_default_color.return_value = (200, 200, 200) rect = Mock() rect.X = 10 rect.Y = 20 rect.Width = 50 rect.GetWidth.return_value = 50 rect.GetHeight.return_value = 8 rect.Get.return_value = (10, 20, 50, 8) group = self.svg._draw_event(event, rect) self.assertSvgEqual(group.getXML(), '<g >\n<rect style="stroke:#8C8C8C; stroke-width:1; fill:#C8C8C8; " height="8" width="50" y="20" x="10" />\n<g clip-path="url(#path10_20_50)" >\n<text style="font-size:9px; font-family:Verdana; stroke-dasharray:(2, 2); text-anchor:middle; " y="25" x="35" lengthAdjust="spacingAndGlyphs" >\nFoo</text>\n</g>\n<polygon style="stroke:#787878; stroke-width:1; fill:#787878; " points="50,20 60,20 60,30" />\n</g>\n')
[docs] def test_can_draw_legend(self): category = Mock() category.color = (127, 127, 127) category.name = "First Category" categories = (category,) group = self.svg._draw_legend(categories) self.assertSvgEqual(group.getXML(), '<g >\n<rect style="stroke:black; stroke-width:1; fill:white; " height="24" width="60" y="171" x="335" />\n<rect style="stroke:#585858; stroke-width:1; fill:#7F7F7F; " height="14" width="14" y="176" x="340" />\n<g clip-path="url(#path357_176_38)" >\n<text style="font-size:9px; font-family:Verdana; stroke-dasharray:(2, 2); text-anchor:left; " y="187" x="360" lengthAdjust="spacingAndGlyphs" >\nFirst Category</text>\n</g>\n</g>\n')
[docs] def test_can_define_shadow_filter(self): d = self.svg._define_shadow_filter() self.assertSvgEqual(d.getXML(), '<defs >\n<filter height="1.9" width="1.9" y="-.5" x="-.3" id="filterShadow" >\n<feGaussianBlur result="out1" in="SourceAlpha" stdDeviation="4" />\n<feOffset dy="-4" result="out2" dx="4" in="out1" />\n<feMerge >\n<feMergeNode in="out2" />\n<feMergeNode in="SourceGraphic" />\n</feMerge>\n</filter>\n</defs>\n')
[docs] def test_legend_should_be_drawn(self): self.svg._appearence.get_legend_visible.return_value = True categories = [] self.assertFalse(self.svg._legend_should_be_drawn(categories)) categories = [""] self.assertTrue(self.svg._legend_should_be_drawn(categories)) self.svg._appearence.get_legend_visible.return_value = False self.assertFalse(self.svg._legend_should_be_drawn(categories))
[docs] def test_can_get_base_color_when_event_has_no_category(self): category = Mock() category.color = (1, 2, 3) event = Mock() event.category = category self.assertEqual((1, 2, 3), self.svg._get_event_color(event))
[docs] def test_can_get_base_color_when_event_has_category(self): event = Mock() event.category = None event.get_default_color.return_value = (200, 200, 200) self.assertEqual((200, 200, 200), self.svg._get_event_color(event))
[docs] def test_can_draw_background(self): self.appearence.get_bg_colour.return_value = (1, 2, 3, 4) rect = self.svg._draw_background() self.assertSvgEqual(rect.getXML(), '<rect style="stroke:black; stroke-width:1; fill:#010203; " height="200" width="400" y="0" x="0" />\n')
[docs] def test_can_draw_era_background(self): def my_side_effect(*args, **kwargs): self.call_count += 1 if self.call_count == 1: return 50 else: return 75 self.scene.x_pos_for_time.side_effect = my_side_effect era = Mock(Era) era.get_color.return_value = (127, 127, 127, 4) era = self.svg._draw_era_strip(era) self.assertSvgEqual(era.getXML(), '<rect style="stroke:black; stroke-width:0; fill:#7F7F7F; " height="194" width="25" y="3" x="50" />\n')
[docs] def test_can_draw_era_text(self): def my_side_effect(*args, **kwargs): self.call_count += 1 if self.call_count == 1: return 50 else: return 75 self.scene.x_pos_for_time.side_effect = my_side_effect era = Mock(Era) era.get_name.return_value = "foobar" text = self.svg._draw_era_text(era) self.assertSvgEqual(text.getXML(), '<text style="font-size:9px; font-family:Verdana; stroke-dasharray:(2, 2); text-anchor:middle; " y="195" x="87" >\nfoobar</text>\n')
[docs] def test_calc_clip_path(self): """ (100, 100) (300, 100) X----------------------------X |(103,103) (297,103)| | x----------------------x | | | | | | x----------------------x | | (103,117) (297,117)| X----------------------------X (100, 120) (300, 120) """ rect = (100, 100, 200, 20) path_id, path = self.svg._calc_clip_path(rect) self.assertEqual('path100_100_200', path_id) self.assertSvgEqual('<path d="M 100 120 H 300 V 100 H 100 " />\n', path.getXML()) rect = (-100, 100, 500, 20) path_id, path = self.svg._calc_clip_path(rect) self.assertEqual('path0_100_400', path_id) self.assertSvgEqual('<path d="M 0 120 H 400 V 100 H 0 " />\n', path.getXML())
[docs] def test_calc_text_pos(self): """ (100, 100) (300, 100) X----------------------------X |(103,103) (297,103)| | x----------------------x | | | | | | x----------------------x | | (103,117) (297,117)| X----------------------------X (100, 120) (300, 120) """ rect = (100, 100, 200, 20) pos = self.svg._calc_text_pos(rect) self.assertEqual((103, 117), pos) pos = self.svg._calc_text_pos(rect, center_text=True) self.assertEqual((200, 117), pos) rect = (-100, 100, 400, 20) pos = self.svg._calc_text_pos(rect) self.assertEqual((0, 117), pos) pos = self.svg._calc_text_pos(rect, center_text=True) self.assertEqual((147, 117), pos)
[docs] def setUp(self): timeline = Mock() self.view_properties = self.setup_view_properties() self.appearence = Mock() self.scene = self.setup_scene() self.svg = SVGDrawingAlgorithm(timeline, self.scene, self.view_properties, self.appearence)
[docs] def setup_view_properties(self): view_properties = Mock(ViewProperties) view_properties.is_selected.return_value = False return view_properties
[docs] def setup_scene(self): self.call_count = 0 scene = Mock(TimelineScene) scene.width = 400 scene.height = 200 scene.divider_y = 200 scene.x_pos_for_now.return_value = 150 scene.x_pos_for_time.return_value = 200 scene.center_text.return_value = False self.point_event = Mock(Event) self.point_event_rect = Mock() self.point_event_rect.Y = 100 self.point_event_rect.Height = 12 scene.event_data = [(self.point_event, self.point_event_rect), ] return scene