test_generate.py 11.8 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

juga's avatar
juga committed
74
75
# TODO: move the following tests to test_v3bwfile?

76

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


95
96
def test_generate_single_success_noscale(dotsbws_success_result, caplog,
                                         parser,  capfd):
97
98
    dotsbws = dotsbws_success_result
    args = parser.parse_args(
99
100
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
101
102
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
103
    dd = conf['paths']['datadir']
104
    # Here results is a dict
105
    results = load_recent_results_in_datadir(1, dd, success_only=False)
106
107
108
109
110
    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]]
111
112
113
114
115
    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')
116
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
117

118
    bw = round(median([dl['amount'] / dl['duration'] / 1024
119
120
                       for dl in result.downloads]))
    rtt = median([round(r * 1000) for r in result.rtts])
juga's avatar
juga committed
121
    bw_line = V3BWLine(result.fingerprint, bw, nick=result.nickname, rtt=rtt,
122
123
                       time=unixts_to_isodt_str(round(result.time)),
                       success=1, error_circ=0, error_misc=0,
juga's avatar
juga committed
124
125
                       error_stream=0)
    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])
juga's avatar
juga committed
153
    bw_line = V3BWLine(result.fingerprint, bw, nick=result.nickname, rtt=rtt,
154
155
                       time=unixts_to_isodt_str(round(result.time)),
                       success=1, error_circ=0, error_misc=0,
juga's avatar
juga committed
156
157
                       error_stream=0)
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
158
159
160


def test_generate_single_relay_success_noscale(
161
        dotsbws_success_result_one_relay, parser, capfd):
162
163
    dotsbws = dotsbws_success_result_one_relay
    args = parser.parse_args(
164
165
        '-d {} --log-level DEBUG generate --output /dev/stdout'
        .format(dotsbws.name).split())
166
167
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
168
    dd = conf['paths']['datadir']
169
    # Here results is a dict
170
    results = load_recent_results_in_datadir(1, dd, success_only=False)
171
172
173
174
175
    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]]
176
177
178
179
180
    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')
181
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
182

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


def test_generate_single_relay_success_scale(
199
        dotsbws_success_result_one_relay, parser, capfd):
200
201
    dotsbws = dotsbws_success_result_one_relay
    args = parser.parse_args(
202
203
        '-d {} --log-level DEBUG generate --scale --output /dev/stdout'
        .format(dotsbws.name).split())
204
205
    conf = get_config(args)
    sbws.core.generate.main(args, conf)
206
    dd = conf['paths']['datadir']
207
    # Here results is a dict
208
    results = load_recent_results_in_datadir(1, dd, success_only=False)
209
210
211
212
213
    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]]
214
215
216
217
218
    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')
219
    assert len(stdout_lines) == 1 + NUM_LINES_HEADER_V110
220
221
222

    speed = 7500
    rtt = round(median([round(r * 1000) for r in result.rtts]))
juga's avatar
juga committed
223
224
    bw_line = V3BWLine(result.fingerprint, speed, nick=result.nickname,
                       rtt=rtt,
225
226
                       time=unixts_to_isodt_str(round(result.time)),
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
227
228
                       error_stream=0)
    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
257
    r1_name = r1_results[0].nickname
    r1_fingerprint = r1_results[0].fingerprint
258
    r1_ed25519 = r1_results[0].ed25519_master_key
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,
265
266
                       time=r1_time,
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
267
268
                       error_stream=0)
    assert stdout_lines[1 + NUM_LINES_HEADER_V110] + '\n' == str(bw_line)
269
    r2_results = [r for r in results if r.fingerprint == 'B' * 40]
270
    r2_time = unixts_to_isodt_str(round(max([r.time for r in r2_results])))
271
272
    r2_name = r2_results[0].nickname
    r2_fingerprint = r2_results[0].fingerprint
273
    r2_ed25519 = r2_results[0].ed25519_master_key
274
275
    r2_speeds = [dl['amount'] / dl['duration'] / 1024
                 for r in r2_results for dl in r.downloads]
276
277
278
    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
279
    bw_line = V3BWLine(r2_fingerprint, r2_speed, nick=r2_name, rtt=r2_rtt,
280
281
                       time=r2_time,
                       success=2, error_circ=0, error_misc=0,
juga's avatar
juga committed
282
283
                       error_stream=0)
    assert stdout_lines[NUM_LINES_HEADER_V110] + '\n' == str(bw_line)