~smspillaz/unity/untiy.less-paint-insanity

« back to all changes in this revision

Viewing changes to tests/autopilot/autopilot/tests/__init__.py

  • Committer: Daniel van Vugt
  • Date: 2012-03-14 06:24:18 UTC
  • mfrom: (2108 unity)
  • mto: This revision was merged to the branch mainline in revision 2146.
  • Revision ID: daniel.van.vugt@canonical.com-20120314062418-nprucpbr0m7qky5e
MergedĀ latestĀ lp:unity

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
"""
2
2
Autopilot tests for Unity.
3
 
"""
 
 
b'\\ No newline at end of file'
 
3
"""
 
4
 
 
5
 
 
6
from compizconfig import Setting, Plugin
 
7
import logging
 
8
import os
 
9
from StringIO import StringIO
 
10
from subprocess import call, Popen, PIPE, STDOUT
 
11
from tempfile import mktemp
 
12
from testscenarios import TestWithScenarios
 
13
from testtools import TestCase
 
14
from testtools.content import text_content
 
15
from testtools.matchers import Equals
 
16
import time
 
17
 
 
18
from autopilot.emulators.bamf import Bamf
 
19
from autopilot.emulators.unity import (
 
20
    set_log_severity,
 
21
    start_log_to_file,
 
22
    reset_logging,
 
23
    )
 
24
from autopilot.emulators.unity.dash import Dash
 
25
from autopilot.emulators.unity.launcher import LauncherController
 
26
from autopilot.emulators.unity.switcher import Switcher
 
27
from autopilot.emulators.unity.workspace import WorkspaceManager
 
28
from autopilot.emulators.X11 import Keyboard, Mouse
 
29
from autopilot.glibrunner import GlibRunner
 
30
from autopilot.globals import (global_context,
 
31
    video_recording_enabled,
 
32
    video_record_directory,
 
33
    )
 
34
from autopilot.keybindings import KeybindingsHelper
 
35
 
 
36
 
 
37
logger = logging.getLogger(__name__)
 
38
 
 
39
 
 
40
class LoggedTestCase(TestWithScenarios, TestCase):
 
41
    """Initialize the logging for the test case."""
 
42
 
 
43
    def setUp(self):
 
44
        self._setUpTestLogging()
 
45
        self._setUpUnityLogging()
 
46
        # The reason that the super setup is done here is due to making sure
 
47
        # that the logging is properly set up prior to calling it.
 
48
        super(LoggedTestCase, self).setUp()
 
49
 
 
50
    def _setUpTestLogging(self):
 
51
        class MyFormatter(logging.Formatter):
 
52
 
 
53
            def formatTime(self, record, datefmt=None):
 
54
                ct = self.converter(record.created)
 
55
                if datefmt:
 
56
                    s = time.strftime(datefmt, ct)
 
57
                else:
 
58
                    t = time.strftime("%H:%M:%S", ct)
 
59
                    s = "%s.%03d" % (t, record.msecs)
 
60
                return s
 
61
 
 
62
        self._log_buffer = StringIO()
 
63
        root_logger = logging.getLogger()
 
64
        root_logger.setLevel(logging.DEBUG)
 
65
        handler = logging.StreamHandler(stream=self._log_buffer)
 
66
        log_format = "%(asctime)s %(levelname)s %(module)s:%(lineno)d - %(message)s"
 
67
        handler.setFormatter(MyFormatter(log_format))
 
68
        root_logger.addHandler(handler)
 
69
        #Tear down logging in a cleanUp handler, so it's done after all other
 
70
        # tearDown() calls and cleanup handlers.
 
71
        self.addCleanup(self._tearDownLogging)
 
72
 
 
73
    def _tearDownLogging(self):
 
74
        logger = logging.getLogger()
 
75
        for handler in logger.handlers:
 
76
            handler.flush()
 
77
            self._log_buffer.seek(0)
 
78
            self.addDetail('test-log', text_content(self._log_buffer.getvalue()))
 
79
            logger.removeHandler(handler)
 
80
        # Calling del to remove the handler and flush the buffer.  We are
 
81
        # abusing the log handlers here a little.
 
82
        del self._log_buffer
 
83
 
 
84
    def _setUpUnityLogging(self):
 
85
        self._unity_log_file_name = mktemp(prefix=self.shortDescription())
 
86
        start_log_to_file(self._unity_log_file_name)
 
87
        self.addCleanup(self._tearDownUnityLogging)
 
88
 
 
89
    def _tearDownUnityLogging(self):
 
90
        reset_logging()
 
91
        with open(self._unity_log_file_name) as unity_log:
 
92
            self.addDetail('unity-log', text_content(unity_log.read()))
 
93
        os.remove(self._unity_log_file_name)
 
94
        self._unity_log_file_name = ""
 
95
 
 
96
    def set_unity_log_level(component, level):
 
97
        """Set the unity log level for 'component' to 'level'.
 
98
 
 
99
        Valid levels are: TRACE, DEBUG, INFO, WARNING and ERROR.
 
100
 
 
101
        Components are dotted unity component names. The empty string specifies
 
102
        the root logging component.
 
103
        """
 
104
        set_log_severity(component, level)
 
105
 
 
106
 
 
107
class VideoCapturedTestCase(LoggedTestCase):
 
108
    """Video capture autopilot tests, saving the results if the test failed."""
 
109
 
 
110
    _recording_app = '/usr/bin/recordmydesktop'
 
111
    _recording_opts = ['--no-sound', '--no-frame', '-o',]
 
112
 
 
113
    def setUp(self):
 
114
        super(VideoCapturedTestCase, self).setUp()
 
115
        global video_recording_enabled
 
116
        if video_recording_enabled and not self._have_recording_app():
 
117
            video_recording_enabled = False
 
118
            logger.warning("Disabling video capture since '%s' is not present", self._recording_app)
 
119
 
 
120
        if video_recording_enabled:
 
121
            self._test_passed = True
 
122
            self.addOnException(self._on_test_failed)
 
123
            self.addCleanup(self._stop_video_capture)
 
124
            self._start_video_capture()
 
125
 
 
126
    def _have_recording_app(self):
 
127
        return os.path.exists(self._recording_app)
 
128
 
 
129
    def _start_video_capture(self):
 
130
        args = self._get_capture_command_line()
 
131
        self._capture_file = self._get_capture_output_file()
 
132
        self._ensure_directory_exists_but_not_file(self._capture_file)
 
133
        args.append(self._capture_file)
 
134
        logger.debug("Starting: %r", args)
 
135
        self._capture_process = Popen(args, stdout=PIPE, stderr=STDOUT)
 
136
 
 
137
    def _stop_video_capture(self):
 
138
        """Stop the video capture. If the test failed, save the resulting file."""
 
139
 
 
140
        if self._test_passed:
 
141
            # We use kill here because we don't want the recording app to start
 
142
            # encoding the video file (since we're removing it anyway.)
 
143
            self._capture_process.kill()
 
144
            self._capture_process.wait()
 
145
        else:
 
146
            self._capture_process.terminate()
 
147
            self._capture_process.wait()
 
148
            if self._capture_process.returncode != 0:
 
149
                self.addDetail('video capture log', text_content(self._capture_process.stdout.read()))
 
150
        self._capture_process = None
 
151
 
 
152
    def _get_capture_command_line(self):
 
153
        return [self._recording_app] + self._recording_opts
 
154
 
 
155
    def _get_capture_output_file(self):
 
156
        return os.path.join(video_record_directory, '%s.ogv' % (self.shortDescription()))
 
157
 
 
158
    def _ensure_directory_exists_but_not_file(self, file_path):
 
159
        dirpath = os.path.dirname(file_path)
 
160
        if not os.path.exists(dirpath):
 
161
            os.makedirs(dirpath)
 
162
        elif os.path.exists(file_path):
 
163
            logger.warning("Video capture file '%s' already exists, deleting.", file_path)
 
164
            os.remove(file_path)
 
165
 
 
166
    def _on_test_failed(self, ex_info):
 
167
        """Called when a test fails."""
 
168
        self._test_passed = False
 
169
 
 
170
 
 
171
class AutopilotTestCase(VideoCapturedTestCase, KeybindingsHelper):
 
172
    """Wrapper around testtools.TestCase that takes care of some cleaning."""
 
173
 
 
174
    run_test_with = GlibRunner
 
175
 
 
176
    KNOWN_APPS = {
 
177
        'Character Map' : {
 
178
            'desktop-file': 'gucharmap.desktop',
 
179
            'process-name': 'gucharmap',
 
180
            },
 
181
        'Calculator' : {
 
182
            'desktop-file': 'gcalctool.desktop',
 
183
            'process-name': 'gcalctool',
 
184
            },
 
185
        'Mahjongg' : {
 
186
            'desktop-file': 'mahjongg.desktop',
 
187
            'process-name': 'mahjongg',
 
188
            },
 
189
        'Remmina' : {
 
190
            'desktop-file': 'remmina.desktop',
 
191
            'process-name': 'remmina',
 
192
            },
 
193
        'Text Editor' : {
 
194
            'desktop-file': 'gedit.desktop',
 
195
            'process-name': 'gedit',
 
196
            },
 
197
        }
 
198
 
 
199
    def setUp(self):
 
200
        super(AutopilotTestCase, self).setUp()
 
201
        self.bamf = Bamf()
 
202
        self.keyboard = Keyboard()
 
203
        self.mouse = Mouse()
 
204
        self.dash = Dash()
 
205
        self.switcher = Switcher()
 
206
        self.workspace = WorkspaceManager()
 
207
        self.launcher = self._get_launcher_controller()
 
208
        self.addCleanup(self.workspace.switch_to, self.workspace.current_workspace)
 
209
        self.addCleanup(Keyboard.cleanup)
 
210
        self.addCleanup(Mouse.cleanup)
 
211
 
 
212
    def start_app(self, app_name):
 
213
        """Start one of the known apps, and kill it on tear down."""
 
214
        logger.info("Starting application '%s'", app_name)
 
215
        app = self.KNOWN_APPS[app_name]
 
216
        self.bamf.launch_application(app['desktop-file'])
 
217
        self.addCleanup(call, ["killall", app['process-name']])
 
218
 
 
219
    def close_all_app(self, app_name):
 
220
        """Close all instances of the app_name."""
 
221
        app = self.KNOWN_APPS[app_name]
 
222
        call(["killall", app['process-name']])
 
223
        super(LoggedTestCase, self).tearDown()
 
224
 
 
225
    def get_app_instances(self, app_name):
 
226
        """Get BamfApplication instances for app_name."""
 
227
        desktop_file = self.KNOWN_APPS[app_name]['desktop-file']
 
228
        return self.bamf.get_running_applications_by_desktop_file(desktop_file)
 
229
 
 
230
    def app_is_running(self, app_name):
 
231
        """Returns true if an instance of the application is running."""
 
232
        apps = self.get_app_instances(app_name)
 
233
        return len(apps) > 0
 
234
 
 
235
    def set_unity_option(self, option_name, option_value):
 
236
        """Set an option in the unity compiz plugin options.
 
237
 
 
238
        The value will be set for the current test only.
 
239
 
 
240
        """
 
241
        self.set_compiz_option("unityshell", option_name, option_value)
 
242
 
 
243
    def set_compiz_option(self, plugin_name, setting_name, setting_value):
 
244
        """Set setting `setting_name` in compiz plugin `plugin_name` to value `setting_value`
 
245
        for one test only.
 
246
        """
 
247
        old_value = self._set_compiz_option(plugin_name, setting_name, setting_value)
 
248
        self.addCleanup(self._set_compiz_option, plugin_name, setting_name, old_value)
 
249
 
 
250
    def _set_compiz_option(self, plugin_name, option_name, option_value):
 
251
        logger.info("Setting compiz option '%s' in plugin '%s' to %r",
 
252
            option_name, plugin_name, option_value)
 
253
        plugin = Plugin(global_context, plugin_name)
 
254
        setting = Setting(plugin, option_name)
 
255
        old_value = setting.Value
 
256
        setting.Value = option_value
 
257
        global_context.Write()
 
258
        return old_value
 
259
 
 
260
    def _get_launcher_controller(self):
 
261
        controllers = LauncherController.get_all_instances()
 
262
        self.assertThat(len(controllers), Equals(1))
 
263
        return controllers[0]