Passed
Push — master ( 021d80...b06592 )
by Stephan
01:02
created

test_process_path_nopad()   A

Complexity

Conditions 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
1
"""Test suite for saveswap.py
2
3
As this relies on helpers from py.test, it must be run with ``py.test``.
4
"""
5
6
from __future__ import (absolute_import, division, print_function,
7
                        with_statement, unicode_literals)
8
9
__author__ = "Stephan Sokolow"
10
__license__ = "MIT"
11
__appname__ = "N64-Saveswap"
12
__version__ = "0.0pre0"
13
14
import os, sys
15
from contextlib import contextmanager
16
17
from saveswap import (calculate_padding, byteswap, main, process_path,
18
                      FileIncomplete, FileTooBig)
19
20
@contextmanager
21
def set_argv(args):
22
    """Context manager to temporarily modify sys.argv"""
23
    old_argv = sys.argv
24
    try:
25
        sys.argv = [sys.argv[0]] + [str(x) for x in args]
26
        yield
27
    finally:
28
        sys.argv = old_argv
29
30
def test_calculate_padding(tmpdir):
31
    """Test that calculate_padding works as expected"""
32
    import pytest
33
    test_file = tmpdir.join("fake_dump")
34
35
    for start, expected in (
36
            (100, 512), (500, 2048), (1000, 32768), (10000, 131072)):
37
        test_file.write("1234" * start)
38
        assert calculate_padding(str(test_file)) == expected
39
40
    test_file.write("1234" * 100000)
41
    with pytest.raises(FileTooBig):
42
        calculate_padding(str(test_file))
43
44
def test_byteswap(tmpdir):
45
    """Test that byteswap produces the expected output"""
46
    test_file = tmpdir.join("fake_dump")
47
48
    # Test the various modes
49
    for options, expected in (
50
            ({}, "4321"),
51
            ({'swap_bytes': False}, "3412"),
52
            ({'swap_words': False}, "2143"),
53
            ({'swap_bytes': False, 'swap_words': False}, "1234")):
54
        test_file.write("1234" * 10)
55
        byteswap(str(test_file), **options)
56
        assert test_file.read() == expected * 10
57
58
def test_byteswap_padding(tmpdir):
59
    """Test that byteswap pads as intended"""
60
    test_file = tmpdir.join("fake_dump")
61
    test_file.write("1234" * 100000)
62
    byteswap(str(test_file), pad_to=500000)
63
    assert test_file.read() == ("4321" * 100000) + ("\x00" * 100000)
64
65
def test_byteswap_with_incomplete(tmpdir):
66
    """Test that byteswap reacts properly to file sizes with remainders
67
68
    (ie. file sizes that are not evenly divisible by 2 or 4)
69
    """
70
    import pytest
71
    test_file = tmpdir.join("fake_dump")
72
73
    # Define a function which will be called for each combination of inputs
74
    def test_callback(_bytes, _words, pad_to):
75
        """Function called many times by _vary_check_swap_inputs"""
76
        # Test that both types of swapping error out on odd-numbered lengths
77
        test_file.write("12345")
78
        if _bytes or _words:
79
            with pytest.raises(FileIncomplete):
80
                byteswap(str(test_file), _bytes, _words, pad_to)
81
82
        test_file.write("123456")
83
        if _words:
84
            with pytest.raises(FileIncomplete):
85
                byteswap(str(test_file), _bytes, _words, pad_to)
86
        else:
87
            byteswap(str(test_file), False, _words, pad_to)
88
89
    # Let _vary_check_swap_inputs call test_callback once for each combination
90
    _vary_check_swap_inputs(test_callback)
91
92
def test_process_path_autopad_error(tmpdir):
93
    """Test that process_path reacts to pad_to=None properly on error"""
94
    import pytest
95
    test_file = tmpdir.join("fake_dump")
96
    backup_path = str(test_file) + '.bak'
97
98
    test_file.write("1234" * 100000)
99
    assert not os.path.exists(backup_path)
100
    with pytest.raises(FileTooBig):
101
        process_path(str(test_file), pad_to=None)
102
    assert test_file.read() == "1234" * 100000  # Unchanged on error
103
    assert not os.path.exists(backup_path)      # No backup on oversize
104
105
def test_process_path_padding(tmpdir):
106
    """Test that process_path pads properly"""
107
    import pytest
108
    test_file = tmpdir.join("fake_dump")
109
    backup_path = str(test_file) + '.bak'
110
111
    test_file.write("1234" * 100000)
112
    process_path(str(test_file), pad_to=500000)
113
    assert test_file.read() == ("4321" * 100000) + ("\x00" * 100000)
114
    assert os.path.exists(backup_path)
115
116
def test_process_path_nopad(tmpdir):
117
    """Test that process_path reacts to pad_to=0 properly"""
118
    import pytest
119
    test_file = tmpdir.join("fake_dump")
120
    backup_path = str(test_file) + '.bak'
121
122
    test_file.write("1234" * 100000)
123
    process_path(str(test_file), pad_to=0)
124
    assert test_file.read() == "4321" * 100000
125
    assert os.path.exists(backup_path)
126
127
def check_main_retcode(args, code):
128
    """Helper for testing return codes from main()"""
129
    try:
130
        with set_argv(args):
131
            main()
132
    except SystemExit as err:
133
        assert err.code == code
134
135
def test_main_works(tmpdir):
136
    """Functional test for basic main() use"""
137
    test_file = tmpdir.join("fake_dump")
138
    backup_path = str(test_file) + '.bak'
139
140
    # Test successful runs
141
    for pat_reps, options, expect_pat, expect_len, backup in (
142
            (500, [], '4321', 2048, False),
143
            (100, ['--swap-mode=words-only'], '3412', 512, True),
144
            (1000, ['--swap-mode=bytes-only'], '2143', 32768, False),
145
            (1000, ['--force-padding=0',
146
                    '--swap-mode=bytes-only'], '2143', 4000, False),
147
            (100000, ['--force-padding=500000'], '4321', 500000, True)):
148
149
        bkopt = [] if backup else ['--no-backup']
150
        test_file.write("1234" * pat_reps)
151
        with set_argv(options + bkopt + [test_file]):
152
            main()
153
        assert test_file.read() == (expect_pat * pat_reps) + (
154
            "\x00" * (expect_len - (4 * pat_reps)))
155
        if backup:
156
            assert os.path.exists(backup_path)
157
            os.remove(backup_path)
158
159
def test_main_missing_file(tmpdir):
160
    """Functional test for main() with nonexistant path"""
161
    missing_path = str(tmpdir.join("missing_file"))
162
    check_main_retcode([missing_path], 10)
163
    assert not os.path.exists(missing_path + '.bak')
164
165
def test_main_error_returns(tmpdir):
166
    """Functional test for main() with erroring input"""
167
    test_file = tmpdir.join("fake_dump")
168
    backup_path = str(test_file) + '.bak'
169
170
    assert not os.path.exists(backup_path)
171
    test_file.write("1234" * 100000)  # Too big
172
    check_main_retcode([test_file], 20)
173
    assert not os.path.exists(backup_path)
174
175
    test_file.write("12345")          # Not evenly disible by 2
176
    check_main_retcode([test_file], 30)
177
    # TODO: Fix the code so the backup is removed on this failure
178
179
def _vary_check_swap_inputs(callback):
180
    """Helper to avoid duplicating stuff within test_byteswap_with_incomplete
181
182
    You want to be careful about this, because the number of tests run goes up
183
    exponentially, but with small numbers of combinations, it's very useful.
184
    """
185
    for _bytes in (True, False):
186
        for _words in (True, False):
187
            for _padding in (0, 1000, 2048):
188
                callback(_bytes, _words, _padding)
189