blob: 60ba9a1689cdc27f1a20ff9af62e291fde438395 [file] [log] [blame]
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +08001# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import re
7
8from autotest_lib.client.bin import utils
9from autotest_lib.client.cros import constants, httpd
10from autotest_lib.server import autotest
11
12
13class DisplayClient(object):
14 """DisplayClient is a layer to control display logic over a remote DUT.
15
16 The Autotest host object representing the remote DUT, passed to this
17 class on initialization, can be accessed from its _client property.
18
19 """
20
21 HTTPD_PORT = 12345
22 X_ENV_VARIABLES = 'DISPLAY=:0.0 XAUTHORITY=/home/chronos/.Xauthority'
23 XMLRPC_CONNECT_TIMEOUT = 30
24
25
26 def __init__(self, host):
27 """Construct a DisplayClient.
28
29 @param host: Host object representing a remote host.
30 """
31 self._client = host
32 self._display_xmlrpc_client = None
33 self._http_listener = None
34 self._server_ip = None
35
36
37 def initialize(self, test_data_dir=None):
38 """Initializes some required servers, like HTTP daemon, RPC connection.
39
40 @param test_data_dir: Path to the test data directory. A HTTP daemon
41 serves this directory as its root. If None, the HTTP daemon
42 doesn't run.
43 """
44 # Make sure the client library is on the device so that the proxy code
45 # is there when we try to call it.
46 client_at = autotest.Autotest(self._client)
47 client_at.install()
48
49 # Start up the XML-RPC proxy on the client.
50 self._display_xmlrpc_client = self._client.xmlrpc_connect(
51 constants.DISPLAY_TESTING_XMLRPC_SERVER_COMMAND,
52 constants.DISPLAY_TESTING_XMLRPC_SERVER_PORT,
53 command_name=(
54 constants.DISPLAY_TESTING_XMLRPC_SERVER_CLEANUP_PATTERN
55 ),
56 ready_test_name=(
57 constants.DISPLAY_TESTING_XMLRPC_SERVER_READY_METHOD),
58 timeout_seconds=self.XMLRPC_CONNECT_TIMEOUT)
59
60 if test_data_dir:
61 # TODO(waihong): Move the test data to a centralized place.
62 self._http_listener = httpd.HTTPListener(
63 port=self.HTTPD_PORT,
64 docroot=os.path.join(test_data_dir, 'calibration_images'))
65 self._http_listener.run()
66 # SSH_CONNECTION is of the form:
67 # [client_ip] [client_port] [server_ip] [server_port]
68 self._server_ip = re.search(
69 r'([0-9.]+) \d+ [0-9.]+ \d+',
70 self._client.run('echo $SSH_CONNECTION').stdout).group(1)
71
72
73 def cleanup(self):
74 """Cleans up."""
75 if self._http_listener:
76 self._http_listener.stop()
77 self._client.rpc_disconnect_all()
78
79
80 def __del__(self):
81 """Destructor of DisplayClient."""
82 self.cleanup()
83
84
85 def get_connector_name(self):
86 """Gets the name of the external output connector.
87
88 @return The external output connector name as a string.
89 """
90 return self._display_xmlrpc_client.get_ext_connector_name()
91
92
93 def load_calibration_image(self, resolution):
94 """Load a full screen calibration image from the HTTP server.
95
96 @param resolution: A tuple (width, height) of resolution.
97 """
98 resolution_str = '%dx%d' % resolution
99 page_url = ('http://%s:%s/%s.png' %
100 (self._server_ip, self.HTTPD_PORT, resolution_str))
101 self._display_xmlrpc_client.load_url(page_url)
102
103
104 def close_tab(self, index=-1):
105 """Closes the tab of the given index.
106
107 @param index: The tab index to close. Defaults to the last tab.
108 """
109 return self._display_xmlrpc_client.close_tab(index)
110
111
112 def set_mirrored(self, is_mirrored):
113 """Sets mirrored mode.
114
115 @param is_mirrored: True or False to indicate mirrored state.
116 """
117 return self._display_xmlrpc_client.set_mirrored(is_mirrored)
118
119
120 def suspend_resume(self):
121 """Suspends the DUT for 10 seconds."""
122 # TODO(waihong): Use other general API instead of this RPC.
123 return self._display_xmlrpc_client.suspend_resume()
124
125
126 def reconnect_output_and_wait(self):
127 """Reconnects output and waits it available."""
128 output = self.get_connector_name()
129 self._display_xmlrpc_client.reconnect_output(output)
130 self._display_xmlrpc_client.wait_output_connected(output)
131 utils.wait_for_value(lambda: (
132 len(self._display_xmlrpc_client.get_display_info())),
133 expected_value=2)
134
135
136 def move_cursor_to_bottom_right(self):
137 """Moves mouse cursor to the bottom-right corner."""
138 output = self.get_connector_name()
139 fb_w, fb_h, _, _ = self._display_xmlrpc_client.get_resolution(output)
140 self._client.run('%s xdotool mousemove %d %d' %
141 (self.X_ENV_VARIABLES, fb_w, fb_h))
142
143
144 def capture_external_screen(self, file_path):
145 """Captures the external screen framebuffer.
146
147 @param file_path: The path of file for output.
148
149 @return: The byte-array for the screen.
150 """
151 output = self.get_connector_name()
152 fb_w, fb_h, fb_x, fb_y = (
153 self._display_xmlrpc_client.get_resolution(output))
154 basename = os.path.basename(file_path)
155 remote_path = os.path.join('/tmp', basename)
156 command = ('%s import -window root -depth 8 -crop %dx%d+%d+%d %s' %
157 (self.X_ENV_VARIABLES, fb_w, fb_h, fb_x, fb_y, remote_path))
158 self._client.run(command)
159 self._client.get_file(remote_path, file_path)
160 return open(file_path).read()