1"""
2Unit tests for nyx.panel.graph.
3"""
4
5import datetime
6import unittest
7
8import stem.control
9
10import nyx.curses
11import nyx.panel.graph
12import test
13
14from test import require_curses
15
16try:
17  # added in python 3.3
18  from unittest.mock import patch
19except ImportError:
20  from mock import patch
21
22EXPECTED_BLANK_GRAPH = """
23Download:
240 B
25
26
27
280 B
29        5s   10   15
30""".rstrip()
31
32EXPECTED_ACCOUNTING = """
33Accounting (awake)                 Time to reset: 01:02
34  4.7 KB / 105.3 KB                  2.0 KB / 9.3 KB
35""".strip()
36
37EXPECTED_GRAPH = """
38Download:
397 KB      *
40          *
413 KB      ** *
42     *    ****
430 B  *********
44         5s   10   15
45""".rstrip()
46
47
48class TestGraphPanel(unittest.TestCase):
49  def test_x_axis_labels(self):
50    test_inputs = {
51      0: {},
52      7: {},
53      10: {5: '25s'},
54      15: {5: '25s', 10: '50'},
55      20: {5: '25s', 10: '50', 15: '1m'},
56      25: {5: '25s', 10: '50', 15: '1m', 20: '1.6'},
57      45: {5: '25s', 10: '50', 15: '1m', 20: '1.6', 25: '2.0', 30: '2.5', 35: '2.9', 40: '3.3'},
58      80: {10: '50s', 20: '1m', 30: '2.5', 40: '3.3', 50: '4.1', 60: '5.0', 70: '5.8'},  # spaced more since wide
59    }
60
61    for width, expected in test_inputs.items():
62      self.assertEqual(expected, nyx.panel.graph._x_axis_labels(nyx.panel.graph.Interval.FIVE_SECONDS, width))
63
64    test_inputs = {
65      nyx.panel.graph.Interval.EACH_SECOND: {
66        10: '10s', 20: '20', 30: '30', 40: '40', 50: '50', 60: '1m', 70: '1.1'
67      }, nyx.panel.graph.Interval.FIVE_SECONDS: {
68        10: '50s', 20: '1m', 30: '2.5', 40: '3.3', 50: '4.1', 60: '5.0', 70: '5.8'
69      }, nyx.panel.graph.Interval.THIRTY_SECONDS: {
70        10: '5m', 20: '10', 30: '15', 40: '20', 50: '25', 60: '30', 70: '35'
71      }, nyx.panel.graph.Interval.MINUTELY: {
72        10: '10m', 20: '20', 30: '30', 40: '40', 50: '50', 60: '1h', 70: '1.1'
73      }, nyx.panel.graph.Interval.FIFTEEN_MINUTE: {
74        10: '2h', 20: '5', 30: '7', 40: '10', 50: '12', 60: '15', 70: '17'
75      }, nyx.panel.graph.Interval.THIRTY_MINUTE: {
76        10: '5h', 20: '10', 30: '15', 40: '20', 50: '1d', 60: '1.2', 70: '1.4'
77      }, nyx.panel.graph.Interval.HOURLY: {
78        10: '10h', 20: '20', 30: '1d', 40: '1.6', 50: '2.0', 60: '2.5', 70: '2.9'
79      }, nyx.panel.graph.Interval.DAILY: {
80        10: '10d', 20: '20', 30: '30', 40: '40', 50: '50', 60: '60', 70: '70'
81      },
82    }
83
84    for interval, expected in test_inputs.items():
85      self.assertEqual(expected, nyx.panel.graph._x_axis_labels(interval, 80))
86
87  def test_y_axis_labels(self):
88    data = nyx.panel.graph.ConnectionStats()
89
90    # check with both even and odd height since that determines an offset in the middle
91
92    self.assertEqual({2: '10', 4: '7', 6: '5', 9: '2', 11: '0'}, nyx.panel.graph._y_axis_labels(12, data.primary, 0, 10))
93    self.assertEqual({2: '10', 4: '6', 6: '3', 8: '0'}, nyx.panel.graph._y_axis_labels(9, data.primary, 0, 10))
94
95    # check where the min and max are the same
96
97    self.assertEqual({2: '0', 11: '0'}, nyx.panel.graph._y_axis_labels(12, data.primary, 0, 0))
98
99  @require_curses
100  @patch('nyx.panel.graph.tor_controller')
101  def test_draw_subgraph_blank(self, tor_controller_mock):
102    tor_controller_mock().get_info.return_value = None
103    data = nyx.panel.graph.BandwidthStats()
104
105    rendered = test.render(nyx.panel.graph._draw_subgraph, data.primary, 0, 30, 7, nyx.panel.graph.Bounds.LOCAL_MAX, nyx.panel.graph.Interval.EACH_SECOND, nyx.curses.Color.CYAN, '*')
106    self.assertEqual(EXPECTED_BLANK_GRAPH, rendered.content)
107
108  @require_curses
109  @patch('nyx.panel.graph.tor_controller')
110  def test_draw_subgraph(self, tor_controller_mock):
111    tor_controller_mock().get_info.return_value = '5430,5430 4210,4210 5510,5510 7100,7100 2000,2000 1750,1750 1880,1880 2500,2500 3770,3770'
112    data = nyx.panel.graph.BandwidthStats()
113
114    rendered = test.render(nyx.panel.graph._draw_subgraph, data.primary, 0, 30, 7, nyx.panel.graph.Bounds.LOCAL_MAX, nyx.panel.graph.Interval.EACH_SECOND, nyx.curses.Color.CYAN, '*')
115    self.assertEqual(EXPECTED_GRAPH, rendered.content)
116
117  @require_curses
118  @patch('nyx.panel.graph.tor_controller')
119  def test_draw_accounting_stats(self, tor_controller_mock):
120    tor_controller_mock().is_alive.return_value = True
121
122    accounting_stat = stem.control.AccountingStats(
123      1410723598.276578,
124      'awake',
125      datetime.datetime(2014, 9, 14, 19, 41),
126      62,
127      4837, 102944, 107781,
128      2050, 7440, 9490,
129    )
130
131    rendered = test.render(nyx.panel.graph._draw_accounting_stats, 0, accounting_stat)
132    self.assertEqual(EXPECTED_ACCOUNTING, rendered.content)
133
134  @require_curses
135  @patch('nyx.panel.graph.tor_controller')
136  def test_draw_accounting_stats_disconnected(self, tor_controller_mock):
137    tor_controller_mock().is_alive.return_value = False
138    rendered = test.render(nyx.panel.graph._draw_accounting_stats, 0, None)
139    self.assertEqual('Accounting: Connection Closed...', rendered.content)
140