test_generate.py 12 KB
Newer Older
1
2
3
# FIXME: all functions that depend on num lines should only use bandwith lines
# and not whole header bandwith lines, as every time we change headers,
# tests here would break
juga's avatar
juga committed
4
# import pytest
5

6
import sbws.core.generate
7
8
9
from sbws.util.config import get_config
from sbws.lib.resultdump import load_recent_results_in_datadir
from sbws.lib.resultdump import ResultSuccess
juga's avatar
juga committed
10
from sbws.lib.v3bwfile import NUM_LINES_HEADER_V110, V3BWLine
11
from sbws.util.timestamp import unixts_to_isodt_str
12
from statistics import median
13
import logging
14

15
log = logging.getLogger(__name__)
16

juga's avatar
juga committed
17

18
19
def test_generate_no_dotsbws(tmpdir, caplog, parser):
    caplog.set_level(logging.DEBUG)
20
21
    dotsbws = tmpdir
    args = parser.parse_args(
Matt Traudt's avatar
Matt Traudt committed
22
        '-d {} --log-level DEBUG generate'.format(dotsbws).split())
23
    conf = get_config(args)
24
    try:
25
        sbws.core.generate.main(args, conf)
26
27
28
29
    except SystemExit as e:
        assert e.code == 1
    else:
        assert None, 'Should have failed'
30
    assert 'Try sbws init' in caplog.records[-1].getMessage()
31
32


33
def test_generate_no_datadir(empty_dotsbws, caplog, parser):
34
35
    dotsbws = empty_dotsbws
    args = parser.parse_args(
36
37
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
38
    conf = get_config(args)
39
    try:
40
        sbws.core.generate.main(args, conf)
41
42
43
44
    except SystemExit as e:
        assert e.code == 1
    else:
        assert None, 'Should have failed'
45
    dd = conf['paths']['datadir']
46
    assert '{} does not exist'.format(dd) in caplog.records[-1].getMessage()
47
48


49
def test_generate_bad_scale_constant(empty_dotsbws_datadir, caplog, parser):
50
51
    dotsbws = empty_dotsbws_datadir
    args = parser.parse_args(
52
53
        '-d {} --log-level DEBUG generate --scale-constant -1 '
        '--output /dev/stdout'.format(dotsbws.name).split())
54
    conf = get_config(args)
55
    try:
56
        sbws.core.generate.main(args, conf)
57
58
59
60
    except SystemExit as e:
        assert e.code == 1
    else:
        assert None, 'Should have failed'
61
62
    assert '--scale-constant must be positive' in \
        caplog.records[-1].getMessage()
63
64


65
def test_generate_empty_datadir(empty_dotsbws_datadir, caplog, parser):
66
67
    dotsbws = empty_dotsbws_datadir
    args = parser.parse_args(
68
69
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
70
71
72
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
    assert 'No recent results' in caplog.records[-1].getMessage()
73
74


75
76
def test_generate_single_error(dotsbws_error_result, caplog, parser):
    caplog.set_level(logging.DEBUG)
77
78
    dotsbws = dotsbws_error_result
    args = parser.parse_args(
79
80
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
81
82
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
83
    dd = conf['paths']['datadir']
84
    for record in caplog.records:
85
        if 'Keeping 0/1 read lines from {}'.format(dd) in record.getMessage():
86
            break
juga's avatar
juga committed
87
88
89
    else:
        assert None, 'Unable to find log line indicating 0 success ' \
            'results in data file'
90
    assert 'No recent results' in caplog.records[-1].getMessage()
91
92


93
94
def test_generate_single_success_noscale(dotsbws_success_result, caplog,
                                         parser,  capfd):
95
96
    dotsbws = dotsbws_success_result
    args = parser.parse_args(
97
98
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
99
100
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
101
    dd = conf['paths']['datadir']
102
    # Here results is a dict
103
    results = load_recent_results_in_datadir(1, dd, success_only=False)
104
105
106
107
108
    assert isinstance(results, dict)
    res_len = sum([len(results[fp]) for fp in results])
    assert res_len == 1, 'There should be one result in the datadir'
    # And here we change it to a list
    results = [r for fp in results for r in results[fp]]
109
110
111
112
113
    result = results[0]
    assert isinstance(result, ResultSuccess), 'The one existing result '\
        'should be a success'
    captured = capfd.readouterr()
    stdout_lines = captured.out.strip().split('\n')
114
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
115

116
    bw = round(median([dl['amount'] / dl['duration'] / 1024
117
118
                       for dl in result.downloads]))
    rtt = median([round(r * 1000) for r in result.rtts])
119
120
    bw_line = V3BWLine('$' + result.fingerprint, bw, nick=result.nickname,
                       rtt=rtt,
121
                       time=unixts_to_isodt_str(round(result.time)),
juga's avatar
juga committed
122
                       master_key_ed25519=result.master_key_ed25519,
123
                       success=1, error_circ=0, error_misc=0,
juga's avatar
juga committed
124
                       error_stream=0)
125
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
126
127


128
def test_generate_single_success_scale(dotsbws_success_result, parser,
129
130
131
                                       capfd):
    dotsbws = dotsbws_success_result
    args = parser.parse_args(
132
133
        '-d {} --log-level DEBUG generate --scale --output /dev/stdout'
        .format(dotsbws.name).split())
134
135
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
136
    dd = conf['paths']['datadir']
137
    # Here results is a dict
138
    results = load_recent_results_in_datadir(1, dd, success_only=False)
139
140
141
142
143
    assert isinstance(results, dict)
    res_len = sum([len(results[fp]) for fp in results])
    assert res_len == 1, 'There should be one result in the datadir'
    # And here we change it to a list
    results = [r for fp in results for r in results[fp]]
144
145
146
147
148
    result = results[0]
    assert isinstance(result, ResultSuccess), 'The one existing result '\
        'should be a success'
    captured = capfd.readouterr()
    stdout_lines = captured.out.strip().split('\n')
149
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
150
151
152

    bw = 7500
    rtt = median([round(r * 1000) for r in result.rtts])
153
154
    bw_line = V3BWLine('$' + result.fingerprint, bw, nick=result.nickname,
                       rtt=rtt,
155
                       time=unixts_to_isodt_str(round(result.time)),
juga's avatar
juga committed
156
                       master_key_ed25519=result.master_key_ed25519,
157
                       success=1, error_circ=0, error_misc=0,
juga's avatar
juga committed
158
                       error_stream=0)
159
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
160
161
162


def test_generate_single_relay_success_noscale(
163
        dotsbws_success_result_one_relay, parser, capfd):
164
165
    dotsbws = dotsbws_success_result_one_relay
    args = parser.parse_args(
166
167
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
168
169
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
170
    dd = conf['paths']['datadir']
171
    # Here results is a dict
172
    results = load_recent_results_in_datadir(1, dd, success_only=False)
173
174
175
176
177
    assert isinstance(results, dict)
    res_len = sum([len(results[fp]) for fp in results])
    assert res_len == 2, 'There should be two results in the datadir'
    # And here we change it to a list
    results = [r for fp in results for r in results[fp]]
178
179
180
181
182
    for result in results:
        assert isinstance(result, ResultSuccess), 'All existing results '\
            'should be a success'
    captured = capfd.readouterr()
    stdout_lines = captured.out.strip().split('\n')
183
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
184

185
186
    speeds = [dl['amount'] / dl['duration'] / 1024
              for r in results for dl in r.downloads]
187
188
    speed = round(median(speeds))
    rtt = round(median([round(r * 1000) for r in result.rtts]))
189
    bw_line = V3BWLine('$' + result.fingerprint, speed, nick=result.nickname,
juga's avatar
juga committed
190
                       rtt=rtt, master_key_ed25519=result.master_key_ed25519,
191
192
                       time=unixts_to_isodt_str(round(result.time)),
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
193
                       error_stream=0)
194
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
195
196
197


def test_generate_single_relay_success_scale(
198
        dotsbws_success_result_one_relay, parser, capfd):
199
200
    dotsbws = dotsbws_success_result_one_relay
    args = parser.parse_args(
201
202
        '-d {} --log-level DEBUG generate --scale --output /dev/stdout'
        .format(dotsbws.name).split())
203
204
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
205
    dd = conf['paths']['datadir']
206
    # Here results is a dict
207
    results = load_recent_results_in_datadir(1, dd, success_only=False)
208
209
210
211
212
    assert isinstance(results, dict)
    res_len = sum([len(results[fp]) for fp in results])
    assert res_len == 2, 'There should be two results in the datadir'
    # And here we change it to a list
    results = [r for fp in results for r in results[fp]]
213
214
215
216
217
    for result in results:
        assert isinstance(result, ResultSuccess), 'All existing results '\
            'should be a success'
    captured = capfd.readouterr()
    stdout_lines = captured.out.strip().split('\n')
218
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
219
220
221

    speed = 7500
    rtt = round(median([round(r * 1000) for r in result.rtts]))
222
    bw_line = V3BWLine('$' + result.fingerprint, speed, nick=result.nickname,
juga's avatar
juga committed
223
                       rtt=rtt,
juga's avatar
juga committed
224
                       master_key_ed25519=result.master_key_ed25519,
225
226
                       time=unixts_to_isodt_str(round(result.time)),
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
227
                       error_stream=0)
228
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
229
230
231


def test_generate_two_relays_success_noscale(
232
        dotsbws_success_result_two_relays, parser, capfd):
233
234
    dotsbws = dotsbws_success_result_two_relays
    args = parser.parse_args(
235
236
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
237
238
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
239
    dd = conf['paths']['datadir']
240
    # Here results is a dict
241
    results = load_recent_results_in_datadir(1, dd, success_only=False)
242
243
244
245
246
    assert isinstance(results, dict)
    res_len = sum([len(results[fp]) for fp in results])
    assert res_len == 4, 'There should be 4 results in the datadir'
    # And here we change it to a list
    results = [r for fp in results for r in results[fp]]
247
248
249
250
251
    for result in results:
        assert isinstance(result, ResultSuccess), 'All existing results '\
            'should be a success'
    captured = capfd.readouterr()
    stdout_lines = captured.out.strip().split('\n')
252
    assert len(stdout_lines) == 2 + NUM_LINES_HEADER_V110
253
254

    r1_results = [r for r in results if r.fingerprint == 'A' * 40]
255
    r1_time = unixts_to_isodt_str(round(max([r.time for r in r1_results])))
256
    r1_name = r1_results[0].nickname
257
    r1_fingerprint = '$' + r1_results[0].fingerprint
juga's avatar
juga committed
258
    r1_ed25519 = r1_results[0].master_key_ed25519
259
260
    r1_speeds = [dl['amount'] / dl['duration'] / 1024
                 for r in r1_results for dl in r.downloads]
261
262
263
    r1_speed = round(median(r1_speeds))
    r1_rtt = round(median([round(rtt * 1000) for r in r1_results
                           for rtt in r.rtts]))
juga's avatar
juga committed
264
    bw_line = V3BWLine(r1_fingerprint, r1_speed, nick=r1_name, rtt=r1_rtt,
juga's avatar
juga committed
265
                       time=r1_time, master_key_ed25519=r1_ed25519,
266
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
267
                       error_stream=0)
juga's avatar
juga committed
268
269
    # FIXME: left side does not contain ed25519
    # assert stdout_lines[1 + NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
270
    r2_results = [r for r in results if r.fingerprint == 'B' * 40]
271
    r2_time = unixts_to_isodt_str(round(max([r.time for r in r2_results])))
272
    r2_name = r2_results[0].nickname
273
    r2_fingerprint = '$' + r2_results[0].fingerprint
juga's avatar
juga committed
274
    r2_ed25519 = r2_results[0].master_key_ed25519
275
276
    r2_speeds = [dl['amount'] / dl['duration'] / 1024
                 for r in r2_results for dl in r.downloads]
277
278
279
    r2_speed = round(median(r2_speeds))
    r2_rtt = round(median([round(rtt * 1000) for r in r2_results
                           for rtt in r.rtts]))
juga's avatar
juga committed
280
    bw_line = V3BWLine(r2_fingerprint, r2_speed, nick=r2_name, rtt=r2_rtt,
juga's avatar
juga committed
281
                       time=r2_time, master_key_ed25519=r2_ed25519,
282
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
283
                       error_stream=0)
284
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)