1# coding=utf-8
2# Copyright (c) 2014, 2016, 2019 Intel Corporation
3
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20# SOFTWARE.
21
22"""Tests for log.py module."""
23
24import collections
25import io
26import sys
27import threading
28
29import pytest
30
31import framework.log as log
32
33# pylint: disable=no-self-use,protected-access
34
35
36@pytest.fixture
37def log_state():
38    """Create a unique state instance per test."""
39    return {'total': 1, 'complete': 0, 'lastlength': 0, 'running': [],
40            'summary': collections.defaultdict(lambda: 0)}
41
42
43class TestLogFactory(object):
44    """Tests for the LogFactory class."""
45
46    class TestGet(object):
47        """Tests for the LogFactory.get method."""
48
49        def test_returns_log(self):
50            """Returns a BaseLog derived instance."""
51            logger = log.LogManager('quiet', 100)
52            log_inst = logger.get()
53            assert isinstance(log_inst, log.BaseLog)
54
55    def test_log_state_update(self):
56        """log.BaseLog.log updates shared state managed by LogManager"""
57        logger = log.LogManager('quiet', 100)
58        log_inst = logger.get()
59        log_inst.start(None)
60        log_inst.log('pass')
61
62        assert logger._state['total'] == 100
63        assert logger._state['summary'] == {'pass': 1}
64        assert logger._state['complete'] == 1
65
66
67class TestQuietLog(object):
68    """Test QuietLog class."""
69
70    class TestOutput(object):
71        """Test the output of the various methods."""
72
73        @pytest.fixture(autouse=True, scope='function')
74        def mock_stdout(self, mocker):
75            mocker.patch.object(sys, 'stdout', io.StringIO())
76
77        def test_log(self, log_state):  # pylint: disable=redefined-outer-name
78            """Test the output of the log method."""
79            quiet = log.QuietLog(log_state, threading.Lock())
80            quiet.start(None)
81            quiet.log('pass')
82            sys.stdout.seek(0)
83
84            actual = sys.stdout.read()
85            assert actual == b'[1/1] pass: 1 -\n'
86
87        def test_summary(self, log_state):  # pylint: disable=redefined-outer-name
88            """Test the output of the summary method."""
89            quiet = log.QuietLog(log_state, threading.Lock())
90            # Call log to set the total correctly, then truncate and remove the
91            # values, so the we can test
92            quiet.start(None)
93            quiet.log('pass')
94            sys.stdout.seek(0)
95            sys.stdout.truncate()
96
97            quiet.summary()
98            sys.stdout.seek(0)
99
100            # Because of the 'lastlength' mechanims there will likely be
101            # trainling whitespace after the the output, it's not useful to
102            # test that here, so just strip it.
103            assert sys.stdout.read().rstrip() == b'[1/1] pass: 1'
104
105        def test_start(self, log_state):  # pylint: disable=redefined-outer-name
106            """Test that the start method doesn't have output."""
107            quiet = log.QuietLog(log_state, threading.Lock())
108            quiet.start(None)
109            quiet.start('foo')
110            sys.stdout.seek(0)
111
112            actual = sys.stdout.read()
113            assert actual == b''
114
115
116class TestVerboseLog(object):
117    """Tests for the VerboseLog class."""
118
119    class TestOutput(object):
120        """Test the output of the various methods."""
121
122        @pytest.fixture(autouse=True, scope='function')
123        def mock_stdout(self, mocker):
124            mocker.patch.object(sys, 'stdout', io.StringIO())
125
126        def test_log(self, log_state):  # pylint: disable=redefined-outer-name
127            """Test the output of the log method."""
128            l = log.VerboseLog(log_state, threading.Lock())
129            l.start('foo')
130            sys.stdout.seek(0)
131            sys.stdout.truncate()
132
133            l.log('pass')
134            sys.stdout.seek(0)
135
136            actual = sys.stdout.read()
137            assert actual == b'pass: foo\n\n[1/1] pass: 1 /\n'
138
139        def test_summary(self, log_state):  # pylint: disable=redefined-outer-name
140            """Test the output of the summary method."""
141            l = log.VerboseLog(log_state, threading.Lock())
142            l.start('foo')
143            l.log('pass')
144            sys.stdout.seek(0)
145            sys.stdout.truncate()
146
147            l.summary()
148            sys.stdout.seek(0)
149
150            assert sys.stdout.read().rstrip() == b'[1/1] pass: 1'
151
152        def test_start(self, log_state):  # pylint: disable=redefined-outer-name
153            """Test that the start method doesn't have output."""
154            l = log.VerboseLog(log_state, threading.Lock())
155            l.start('foo')
156            sys.stdout.seek(0)
157
158            assert sys.stdout.read().rstrip() == b'running: foo\n\n[0/1]  \\'
159
160
161class TestDummyLog(object):
162    """Tests for the DummyLog class."""
163
164    class TestOutput(object):
165        """Test the output of the various methods."""
166
167        @pytest.fixture(autouse=True, scope='function')
168        def mock_stdout(self, mocker):
169            mocker.patch.object(sys, 'stdout', io.StringIO())
170
171        def test_log(self, log_state):  # pylint: disable=redefined-outer-name
172            """Test the output of the log method."""
173            quiet = log.DummyLog(log_state, threading.Lock())
174            quiet.start(None)
175            quiet.log('pass')
176            sys.stdout.seek(0)
177
178            actual = sys.stdout.read()
179            assert actual == b''
180
181        def test_summary(self, log_state):  # pylint: disable=redefined-outer-name
182            """Test the output of the summary method."""
183            quiet = log.DummyLog(log_state, threading.Lock())
184            quiet.summary()
185            sys.stdout.seek(0)
186
187            actual = sys.stdout.read()
188            assert actual == b''
189
190        def test_start(self, log_state):  # pylint: disable=redefined-outer-name
191            """Test that the start method doesn't have output."""
192            quiet = log.DummyLog(log_state, threading.Lock())
193            quiet.start('foo')
194            sys.stdout.seek(0)
195
196            actual = sys.stdout.read()
197            assert actual == b''
198