1# test_files.py
2
3import locale
4
5import pytest
6
7from graphviz.files import Source
8
9SOURCE = {
10    'source': 'digraph { hello -> world }',
11    'filename': 'hello.gv', 'directory': 'test-output',
12    'format': 'PNG', 'engine': 'NEATO', 'encoding': 'utf-8',
13}
14
15
16@pytest.fixture(scope='module')
17def source():
18    return Source(**SOURCE)
19
20
21def test_format(source):
22    assert not SOURCE['format'].islower()
23
24    assert source.format == SOURCE['format'].lower()
25    with pytest.raises(ValueError, match=r'format'):
26        source.format = ''
27
28
29def test_engine(source):
30    assert not SOURCE['engine'].islower()
31
32    assert source.engine == SOURCE['engine'].lower()
33    with pytest.raises(ValueError, match=r'engine'):
34        source.engine = ''
35
36
37def test_encoding(source):
38    assert source.encoding == SOURCE['encoding']
39
40    with pytest.raises(LookupError, match=r'encoding'):
41        source.encoding = ''
42
43
44def test_encoding_none(source):
45    source_copy = source.copy()
46    source_copy.encoding = None
47    assert source_copy.encoding == locale.getpreferredencoding()
48
49
50def test_init(source):
51    assert source.source == SOURCE['source']
52    assert source.filename == SOURCE['filename']
53    assert source.directory == SOURCE['directory']
54
55
56def test_init_filename():
57    assert Source('').filename == 'Source.gv'
58    assert type('Named', (Source,), {'name': 'name'})('').filename == 'name.gv'
59
60
61def test__repr_svg_(mocker, source):
62    kwargs = {'return_value.decode.return_value': mocker.sentinel.decoded}
63    pipe = mocker.patch.object(source, 'pipe', new_callable=mocker.Mock, **kwargs)
64
65    assert source._repr_svg_() is mocker.sentinel.decoded
66
67    pipe.assert_called_once_with(format='svg')
68    pipe.return_value.decode.assert_called_once_with(source.encoding)
69
70
71def test_pipe_format(pipe, source, format_='svg'):
72    assert source.format != format_
73
74    assert source.pipe(format=format_) is pipe.return_value
75
76    data = source.source.encode(source.encoding)
77    pipe.assert_called_once_with(source.engine, format_, data, None, None)
78
79
80def test_pipe(pipe, source):
81    assert source.pipe() is pipe.return_value
82
83    data = source.source.encode(source.encoding)
84    pipe.assert_called_once_with(source.engine, source.format, data, None, None)
85
86
87def test_filepath(test_platform, source):
88    if test_platform == 'windows':
89        assert source.filepath == 'test-output\\hello.gv'
90    else:
91        assert source.filepath == 'test-output/hello.gv'
92
93
94def test_save(mocker, py2, filename='filename', directory='directory'):
95    source = Source(**SOURCE)
96    makedirs = mocker.patch('os.makedirs', new_callable=mocker.Mock)
97    open_ = mocker.patch('io.open', mocker.mock_open())
98
99    assert source.save(filename, directory) == source.filepath
100
101    assert source.filename == filename and source.directory == directory
102    if py2:
103        makedirs.assert_called_once_with(source.directory, 0o777)
104    else:
105        makedirs.assert_called_once_with(source.directory, 0o777, exist_ok=True)
106    open_.assert_called_once_with(source.filepath, 'w', encoding=source.encoding)
107    assert open_.return_value.write.call_args_list == [((source.source,),), ((u'\n',),)]
108
109
110def test_render(mocker, render, source):
111    save = mocker.patch.object(source, 'save', new_callable=mocker.Mock)
112    _view = mocker.patch.object(source, '_view', new_callable=mocker.Mock)
113    remove = mocker.patch('os.remove', new_callable=mocker.Mock)
114
115    assert source.render(cleanup=True, view=True) is render.return_value
116
117    save.assert_called_once_with(None, None)
118    render.assert_called_once_with(source.engine, source.format, save.return_value, None, None)
119    remove.assert_called_once_with(save.return_value)
120    _view.assert_called_once_with(render.return_value, source.format)
121
122
123def test_view(mocker, source):
124    render = mocker.patch.object(source, 'render', new_callable=mocker.Mock)
125    kwargs = {'filename': 'filename', 'directory': 'directory', 'cleanup': True}
126
127    assert source.view(**kwargs) is render.return_value
128
129    render.assert_called_once_with(view=True, **kwargs)
130
131
132def test__view(mocker, platform, source):
133    if platform == 'nonplatform':
134        with pytest.raises(RuntimeError, match=r'support'):
135            source._view('name', 'png')
136    else:
137        _view_platform = mocker.patch.object(source, '_view_%s' % platform,
138                                             new_callable=mocker.Mock)
139
140        assert source._view(mocker.sentinel.name, 'png') is None
141
142        _view_platform.assert_called_once_with(mocker.sentinel.name)
143
144
145def test_copy(source):
146    assert source.copy() is not source
147    assert source.copy() is not source.copy()
148    assert source.copy().__class__ is source.__class__
149    assert source.copy().__dict__ == source.__dict__
150
151
152def test_from_file(tmpdir, filename='hello.gv', directory='source_hello',
153                   data=u'digraph { hello -> world }', encoding='utf-8'):
154    lpath = tmpdir.mkdir(directory)
155    (lpath / filename).write_text(data, encoding=encoding)
156
157    source = Source.from_file(filename, str(lpath))
158    assert source.encoding == 'utf-8'
159
160    source = Source.from_file(filename, str(lpath), encoding=None)
161    assert source.encoding == locale.getpreferredencoding()
162
163    source = Source.from_file(filename, str(lpath), encoding=encoding)
164    assert source.source == data
165    assert source.filename == filename
166    assert source.directory == str(lpath)
167    assert source.encoding == encoding
168