Merge lp:~gerboland/unity-2d/testability-target-host-split into lp:unity-2d
- testability-target-host-split
- Merge into trunk
| Status: | Merged |
|---|---|
| Approved by: | Albert Astals Cid on 2012-01-24 |
| Approved revision: | 878 |
| Merged at revision: | 863 |
| Proposed branch: | lp:~gerboland/unity-2d/testability-target-host-split |
| Merge into: | lp:unity-2d |
| Diff against target: |
1215 lines (+586/-136) 17 files modified
tests/README (+9/-9) tests/launcher/autohide_show_tests.rb (+7/-7) tests/launcher/visual_verification.rb (+4/-5) tests/misc/lib/testhelper.rb (+1/-1) tests/misc/lib/tmpwindow.rb (+8/-4) tests/misc/lib/xdo/_xdo.rb (+74/-1) tests/misc/lib/xdo/clipboard.rb (+15/-13) tests/misc/lib/xdo/keyboard.rb (+9/-17) tests/misc/lib/xdo/mouse.rb (+6/-5) tests/misc/lib/xdo/test/test_keyboard.rb (+2/-0) tests/misc/lib/xdo/test/test_tdriver_clipboard.rb (+56/-0) tests/misc/lib/xdo/test/test_tdriver_keyboard.rb (+130/-0) tests/misc/lib/xdo/test/test_tdriver_mouse.rb (+36/-0) tests/misc/lib/xdo/test/test_tdriver_xwindow.rb (+105/-0) tests/misc/lib/xdo/test/test_xdo.rb (+63/-0) tests/misc/lib/xdo/xwindow.rb (+54/-74) tests/run-tests.rb (+7/-0) |
| To merge this branch: | bzr merge lp:~gerboland/unity-2d/testability-target-host-split |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Albert Astals Cid | 2012-01-23 | Pending | |
|
Review via email:
|
|||
Commit Message
Description of the Change
[tests] Modify XDo library and all tests to respect target/host division
Purpose: for autopilot testing, have the test scripts and the software-under-test communicate via qttasserver only. This includes all XDo operations, system calls and file/folder manipulation that test scripts perform. With this, only a few essential dependencies are required on the target, and all ruby code is executed on the host.
Changes made:
- Add generic XDo.execute method to handle all shell commands.
- Add XDo.sut property to specify if running via qttasserver or not
- XDoTool library uses XDo.execute method for all shell commands.
- Tests added for XDoTool library running via qttasserver
- Establish single SUT connection in run-tests.rb as $SUT, pass it to XDo
- Modify tmpwindow class to use $SUT.execute_
- Modify all existing tests to use $SUT.execute_
| Gerry Boland (gerboland) wrote : | # |
| Gerry Boland (gerboland) wrote : | # |
| Albert Astals Cid (aacid) wrote : | # |
You miss a new you need to
sudo add-apt-repository ppa:gerboland/
sudo apt-get update
on the target machine so that testability-qttas is available.
| Albert Astals Cid (aacid) wrote : | # |
s/new/note saying
| Albert Astals Cid (aacid) wrote : | # |
The described way works, some notes on my side though:
* If you want to see the pointer actually moving in the target disable mouse integration (you can do that by right clicking in the little mouse cursor that appears in the lower right part of the VM statusbar)
* If you run your VM in bridged network mode instead of NAT mode there is no need to setup the forwarding of the ports neither in /etc/tdriver/
| Unity Merger (unity-merger) wrote : | # |
Attempt to merge into lp:unity-2d failed due to conflicts:
text conflict in tests/run-tests.rb
| Gerry Boland (gerboland) wrote : | # |
Good comments all, thank you.
Merge conflict fixed. Feel free to approve.
- 878. By Gerry Boland on 2012-01-24
-
Merge trunk, fix conflict
Preview Diff
| 1 | === modified file 'tests/README' |
| 2 | --- tests/README 2012-01-23 21:45:03 +0000 |
| 3 | +++ tests/README 2012-01-24 11:51:24 +0000 |
| 4 | @@ -35,8 +35,7 @@ |
| 5 | - GUI to allow easy inspection of SUT and writing tests [tdriver-visualizer], |
| 6 | but not needed for running tests. |
| 7 | |
| 8 | -For the moment, the system is designed assuming that the SUT is also the Host |
| 9 | -machine. |
| 10 | +For all tests, the SUT is assumed to *not* be the Host machine. |
| 11 | |
| 12 | |
| 13 | Testability installation instructions |
| 14 | @@ -131,8 +130,7 @@ |
| 15 | # Run before each test case begins |
| 16 | setup do |
| 17 | # Execute the application |
| 18 | - @sut = TDriver.sut(:Id => "sut_qt") |
| 19 | - @app = @sut.run( :name => "/absolute/path/to/application", |
| 20 | + @app = $SUT.run( :name => "/absolute/path/to/application", |
| 21 | :arguments => "-testability", |
| 22 | :sleeptime => 2 ) |
| 23 | end |
| 24 | @@ -141,18 +139,20 @@ |
| 25 | teardown do |
| 26 | #@app.close |
| 27 | #Need to kill Launcher as it does not shutdown when politely asked |
| 28 | - system "pkill -nf unity-2d-launcher" |
| 29 | + $SUT.execute_shell_command "pkill -nf unity-2d-launcher" |
| 30 | end |
| 31 | |
| 32 | ############################################################################## |
| 33 | # Test cases |
| 34 | |
| 35 | test "Short description of first test case" do |
| 36 | - assert_equal( Integer(@app.Unity2dPanel()['width']), 66, |
| 37 | - 'These two values are not equal' ) |
| 38 | + verify_equal( 66, TIMEOUT, 'These two values are not equal'){ |
| 39 | + @app.Unity2dPanel()['width']).to_i |
| 40 | + } |
| 41 | |
| 42 | - assert( @app.Unity2dPanel()['x_absolute'], \ |
| 43 | - 'This quantity is not true' ) |
| 44 | + verify_equal( 'true', TIMEOUT, 'This quantity is not true'){ |
| 45 | + @app.Unity2dPanel()['x_absolute'] |
| 46 | + } |
| 47 | end |
| 48 | |
| 49 | test "Another test case description" do |
| 50 | |
| 51 | === modified file 'tests/launcher/autohide_show_tests.rb' |
| 52 | --- tests/launcher/autohide_show_tests.rb 2012-01-19 17:29:13 +0000 |
| 53 | +++ tests/launcher/autohide_show_tests.rb 2012-01-24 11:51:24 +0000 |
| 54 | @@ -35,8 +35,8 @@ |
| 55 | |
| 56 | # Run once at the beginning of this test suite |
| 57 | startup do |
| 58 | - system 'killall unity-2d-launcher > /dev/null 2>&1' |
| 59 | - system 'killall unity-2d-launcher > /dev/null 2>&1' |
| 60 | + $SUT.execute_shell_command 'killall unity-2d-launcher' |
| 61 | + $SUT.execute_shell_command 'killall unity-2d-launcher' |
| 62 | |
| 63 | # Minimize all windows |
| 64 | XDo::XWindow.toggle_minimize_all |
| 65 | @@ -51,11 +51,10 @@ |
| 66 | #Ensure mouse out of the way |
| 67 | XDo::Mouse.move(200,200,10,true) |
| 68 | |
| 69 | - launcher_favorites = `gsettings get com.canonical.Unity.Launcher favorites` |
| 70 | + launcher_favorites = $SUT.execute_shell_command 'gsettings get com.canonical.Unity.Launcher favorites' |
| 71 | |
| 72 | # Execute the application |
| 73 | - @sut = TDriver.sut(:Id => "sut_qt") |
| 74 | - @app = @sut.run( :name => UNITY_2D_LAUNCHER, |
| 75 | + @app = $SUT.run( :name => UNITY_2D_LAUNCHER, |
| 76 | :arguments => "-testability", |
| 77 | :sleeptime => 2 ) |
| 78 | # Make certain application is ready for testing |
| 79 | @@ -67,8 +66,8 @@ |
| 80 | TmpWindow.close_all_windows |
| 81 | #@app.close |
| 82 | #Need to kill Launcher as it does not shutdown when politely asked |
| 83 | - system "pkill -nf unity-2d-launcher" |
| 84 | - system "gsettings set com.canonical.Unity.Launcher favorites \"" + launcher_favorites + "\"" |
| 85 | + $SUT.execute_shell_command 'pkill -nf unity-2d-launcher' |
| 86 | + $SUT.execute_shell_command "gsettings set com.canonical.Unity.Launcher favorites \"" + launcher_favorites + "\"" |
| 87 | end |
| 88 | |
| 89 | ##################################################################################### |
| 90 | @@ -246,6 +245,7 @@ |
| 91 | @app.Unity2dPanel()['x_absolute'].to_i |
| 92 | } |
| 93 | |
| 94 | + sleep 1 #launcher seems not ready to accept Super key, need a pause |
| 95 | XDo::Keyboard.key_down('SUPER') |
| 96 | verify_equal( 0, TIMEOUT, 'Launcher hiding when Super Key held, should be visible' ) { |
| 97 | @app.Unity2dPanel()['x_absolute'].to_i |
| 98 | |
| 99 | === modified file 'tests/launcher/visual_verification.rb' |
| 100 | --- tests/launcher/visual_verification.rb 2011-12-12 22:57:15 +0000 |
| 101 | +++ tests/launcher/visual_verification.rb 2012-01-24 11:51:24 +0000 |
| 102 | @@ -30,8 +30,8 @@ |
| 103 | |
| 104 | # Run once at the beginning of this test suite |
| 105 | startup do |
| 106 | - system 'killall unity-2d-launcher > /dev/null 2>&1' |
| 107 | - system 'killall unity-2d-launcher > /dev/null 2>&1' |
| 108 | + $SUT.execute_shell_command 'killall unity-2d-launcher' |
| 109 | + $SUT.execute_shell_command 'killall unity-2d-launcher' |
| 110 | end |
| 111 | |
| 112 | # Run once at the end of this test suite |
| 113 | @@ -41,8 +41,7 @@ |
| 114 | # Run before each test case begins |
| 115 | setup do |
| 116 | # Execute the application |
| 117 | - @sut = TDriver.sut(:Id => "sut_qt") |
| 118 | - @app = @sut.run( :name => UNITY_2D_LAUNCHER, |
| 119 | + @app = $SUT.run( :name => UNITY_2D_LAUNCHER, |
| 120 | :arguments => "-testability", |
| 121 | :sleeptime => 2 ) |
| 122 | end |
| 123 | @@ -51,7 +50,7 @@ |
| 124 | teardown do |
| 125 | #@app.close |
| 126 | #Need to kill Launcher as it does not shutdown when politely asked |
| 127 | - system "pkill -nf unity-2d-launcher" |
| 128 | + $SUT.execute_shell_command 'pkill -nf unity-2d-launcher' |
| 129 | end |
| 130 | |
| 131 | ##################################################################################### |
| 132 | |
| 133 | === modified file 'tests/misc/lib/testhelper.rb' |
| 134 | --- tests/misc/lib/testhelper.rb 2012-01-23 22:10:02 +0000 |
| 135 | +++ tests/misc/lib/testhelper.rb 2012-01-24 11:51:24 +0000 |
| 136 | @@ -1,7 +1,7 @@ |
| 137 | require 'test/unit' |
| 138 | |
| 139 | dir = File.dirname(File.expand_path(__FILE__)) |
| 140 | -$LOAD_PATH.unshift dir + '/../lib' |
| 141 | +$LOAD_PATH.unshift File.expand_path(dir + '/../lib') |
| 142 | $TEST_DIR = File.dirname(File.expand_path(__FILE__)) |
| 143 | |
| 144 | # Enable a startup and shutdwn method for each test case |
| 145 | |
| 146 | === modified file 'tests/misc/lib/tmpwindow.rb' |
| 147 | --- tests/misc/lib/tmpwindow.rb 2012-01-18 09:12:23 +0000 |
| 148 | +++ tests/misc/lib/tmpwindow.rb 2012-01-24 11:51:24 +0000 |
| 149 | @@ -42,16 +42,20 @@ |
| 150 | # TmpWindow.open_window_at(200, 300) |
| 151 | def self.open_window_at(x=100,y=100) |
| 152 | window_id = -1 |
| 153 | + # Generate a temporary directory (used to generate unique window title to help Xdo get window ID) |
| 154 | + t = Time.now.strftime("%Y%m%d") |
| 155 | + path = "/tmp/#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" |
| 156 | + $SUT.execute_shell_command('mkdir -p ' + path) |
| 157 | # Open Terminal with position (x,y) |
| 158 | - Dir.mktmpdir {|dir| # use this to generate unique window title to help Xdo get window ID |
| 159 | - system "gnome-terminal --geometry=100x30+#{x}+#{y} --working-directory=#{dir} &" |
| 160 | - Timeout.timeout(30){ window_id = XDo::XWindow.wait_for_window(dir)} |
| 161 | - } |
| 162 | + $SUT.execute_shell_command("gnome-terminal --geometry=100x30+#{x}+#{y} --working-directory=#{path}", :detached => true) |
| 163 | + Timeout.timeout(30){ window_id = XDo::XWindow.wait_for_window(path)} |
| 164 | Kernel.raise(SystemCallError, "Unable to open gnome-terminal") if window_id == -1 |
| 165 | xid = XDo::XWindow.new(window_id) |
| 166 | # create list of windows opened |
| 167 | @xid_list = [] if @xid_list == nil |
| 168 | @xid_list << xid |
| 169 | + # remove temporary directory |
| 170 | + $SUT.execute_shell_command('rmdir ' + path) |
| 171 | xid |
| 172 | end |
| 173 | |
| 174 | |
| 175 | === modified file 'tests/misc/lib/xdo/_xdo.rb' |
| 176 | --- tests/misc/lib/xdo/_xdo.rb 2012-01-04 16:09:14 +0000 |
| 177 | +++ tests/misc/lib/xdo/_xdo.rb 2012-01-24 11:51:24 +0000 |
| 178 | @@ -34,5 +34,78 @@ |
| 179 | |
| 180 | class ParseError < StandardError |
| 181 | end |
| 182 | - |
| 183 | + |
| 184 | + #TDriver SUT connection |
| 185 | + def self.sut=(input) |
| 186 | + @sut = input |
| 187 | + end |
| 188 | + |
| 189 | + def self.sut |
| 190 | + @sut |
| 191 | + end |
| 192 | + |
| 193 | + # Execute a shell command. If :sut is set, Testability Driver is used to execute |
| 194 | + # the command on the system under test. Else is run on current machine. |
| 195 | + def self.execute(cmd, timeout=3) |
| 196 | + stdout = '' |
| 197 | + stderr = '' |
| 198 | + return_code = -1 |
| 199 | + |
| 200 | + if @sut == nil |
| 201 | + # Running command in shell. Open3 doesn't allow easy access to return code, so let's use a little bash-fu |
| 202 | + stdin, out, err = Open3.popen3(cmd + '; echo "\n$?"') |
| 203 | + stdout = out.read.strip |
| 204 | + stderr = err.read.strip |
| 205 | + |
| 206 | + #remove last line of stderr and get return code from it |
| 207 | + return_code = stdout.split("\n")[stdout.lines.count-1].to_i |
| 208 | + stdout.chomp!(return_code.to_s) |
| 209 | + stdout.chomp!("\n") |
| 210 | + else |
| 211 | + # Tdriver has the shell_command method to extract stdout, stderr and return number from a shell command |
| 212 | + # run with execute_shell_command. However it requires the shell command to be detached and placed |
| 213 | + # in a separate thread. In my experiments execution and return takes about 2 seconds, whereas a simple |
| 214 | + # synchroneous execution takes mere miliseconds. This wrapper is a bit of bash-fu to split stdout |
| 215 | + # and stderr and obtain the return code while executing it synchroneously. We run new bash instance as |
| 216 | + # command is run in a QProcess which takes all stdout and stderr output from us. |
| 217 | + # |
| 218 | + # To explain the Bash-fu, reading from the outside in (ref: http://tinyurl.com/6ndfm8d): |
| 219 | + # - creates a file descriptor $out for the whole block, duplicating stdout |
| 220 | + # - captures the stdout of the whole command in $error (but see below) |
| 221 | + # - the command itself redirects stderr to stdout (which gets captured above) then stdout to the original |
| 222 | + # stdout from outside the block, so only the stderr gets captured |
| 223 | + |
| 224 | + wrap_cmd = 'bash -c "{ error=$(' + cmd + ' 2>&1 1>&$out); } {out}>&1; echo -n \"==return-code:$?==${error}\""' |
| 225 | + |
| 226 | + # Bug workaround: the command is sent to the qttasserver via xml. The command isn't escaped by |
| 227 | + # execute_system_command. Need to escape certain chars of the command ourselves. |
| 228 | + wrap_cmd.gsub!("&", "&") |
| 229 | + wrap_cmd.gsub!("<", "<") |
| 230 | + wrap_cmd.gsub!(">", ">") |
| 231 | + |
| 232 | + output = @sut.execute_shell_command(wrap_cmd, :timeout => timeout) |
| 233 | + #Now parse output |
| 234 | + errors_exist = false |
| 235 | + |
| 236 | + output.strip.each_line do |line| |
| 237 | + |
| 238 | + #look for "==return-code:" |
| 239 | + if line.match( /^(.*)==return-code:(\d+)==(.*)/ ) and errors_exist == false |
| 240 | + return_code = $2.to_i |
| 241 | + errors_exist = true |
| 242 | + stdout.concat($1) |
| 243 | + stderr.concat($3) |
| 244 | + next |
| 245 | + end |
| 246 | + |
| 247 | + stderr.concat(line) if errors_exist |
| 248 | + stdout.concat(line) unless errors_exist |
| 249 | + end |
| 250 | + |
| 251 | + stderr.strip! |
| 252 | + end |
| 253 | + |
| 254 | + return return_code, stdout, stderr |
| 255 | + end |
| 256 | + |
| 257 | end #module XDo |
| 258 | |
| 259 | === modified file 'tests/misc/lib/xdo/clipboard.rb' |
| 260 | --- tests/misc/lib/xdo/clipboard.rb 2011-12-07 18:39:44 +0000 |
| 261 | +++ tests/misc/lib/xdo/clipboard.rb 2012-01-24 11:51:24 +0000 |
| 262 | @@ -103,9 +103,9 @@ |
| 263 | from.concat([:clipboard, :primary, :secondary]) if from.empty? |
| 264 | |
| 265 | hsh = {} |
| 266 | - hsh[:primary] = `#{XSEL}` if from.include? :primary |
| 267 | - hsh[:clipboard] = `#{XSEL} -b` if from.include? :clipboard |
| 268 | - hsh[:secondary] = `#{XSEL} -s` if from.include? :secondary |
| 269 | + r, hsh[:primary], e = XDo.execute("#{XSEL}") if from.include? :primary |
| 270 | + r, hsh[:clipboard], e = XDo.execute("#{XSEL} -b") if from.include? :clipboard |
| 271 | + r, hsh[:secondary], e = XDo.execute("#{XSEL} -s") if from.include? :secondary |
| 272 | hsh |
| 273 | end |
| 274 | |
| 275 | @@ -127,10 +127,11 @@ |
| 276 | to = to.first.keys |
| 277 | end |
| 278 | to << :clipboard if to.empty? |
| 279 | + text.gsub("'", "\'").gsub("\n", '\n').gsub("\t", '\t') |
| 280 | |
| 281 | - IO.popen("xsel -i", "w"){|io| io.write(text)} if to.include? :primary |
| 282 | - IO.popen("xsel -b -i", "w"){|io| io.write(text)} if to.include? :clipboard |
| 283 | - IO.popen("xsel -s -i", "w"){|io| io.write(text)} if to.include? :secondary |
| 284 | + XDo.execute("echo -n '#{text}' | xsel -i") if to.include? :primary |
| 285 | + XDo.execute("echo -n '#{text}' | xsel -b -i") if to.include? :clipboard |
| 286 | + XDo.execute("echo -n '#{text}' | xsel -s -i") if to.include? :secondary |
| 287 | text |
| 288 | end |
| 289 | |
| 290 | @@ -156,10 +157,11 @@ |
| 291 | to = to.first.keys |
| 292 | end |
| 293 | to << :clipboard if to.empty? |
| 294 | - |
| 295 | - IO.popen("xsel -a -i", "w"){|io| io.write(text)} if to.include? :primary |
| 296 | - IO.popen("xsel -b -a -i", "w"){|io| io.write(text)} if to.include? :clipboard |
| 297 | - IO.popen("xsel -s -a -i", "w"){|io| io.write(text)} if to.include? :secondary |
| 298 | + text.gsub("'", "\'").gsub("\n", '\n').gsub("\t", '\t') |
| 299 | + |
| 300 | + XDo.execute("echo -n '#{text}' | xsel -a -i") if to.include? :primary |
| 301 | + XDo.execute("echo -n '#{text}' | xsel -b -a -i") if to.include? :clipboard |
| 302 | + XDo.execute("echo -n '#{text}' | xsel -s -a -i") if to.include? :secondary |
| 303 | end |
| 304 | |
| 305 | #Clears the specified clipboards. |
| 306 | @@ -182,9 +184,9 @@ |
| 307 | end |
| 308 | clips.concat([:primary, :clipboard, :secondary]) if clips.empty? |
| 309 | |
| 310 | - `#{XSEL} -c` if clips.include? :primary |
| 311 | - `#{XSEL} -b -c` if clips.include? :clipboard |
| 312 | - `#{XSEL} -s -c` if clips.include? :secondary |
| 313 | + XDo.execute("#{XSEL} -c") if clips.include? :primary |
| 314 | + XDo.execute("#{XSEL} -b -c") if clips.include? :clipboard |
| 315 | + XDo.execute("#{XSEL} -s -c") if clips.include? :secondary |
| 316 | nil |
| 317 | end |
| 318 | |
| 319 | |
| 320 | === modified file 'tests/misc/lib/xdo/keyboard.rb' |
| 321 | --- tests/misc/lib/xdo/keyboard.rb 2012-01-04 16:31:43 +0000 |
| 322 | +++ tests/misc/lib/xdo/keyboard.rb 2012-01-24 11:51:24 +0000 |
| 323 | @@ -94,11 +94,9 @@ |
| 324 | #===Remarks |
| 325 | #This function is a bit faster then #simulate. |
| 326 | def type(str, w_id = 0) |
| 327 | - out = Open3.popen3("#{XDOTOOL} type #{w_id.nonzero? ? "--window #{w_id.to_i} " : ""}'#{str}'") do |stdin, stdout, stderr| |
| 328 | - stdin.close_write |
| 329 | - str = stderr.read |
| 330 | - warn(str) unless str.empty? |
| 331 | - end |
| 332 | + return nil if str == "\303" and RUBY_VERSION < '1.9' #deliberately work around Ruby 1.8's bad handling of UTF-8 characters |
| 333 | + return_code, out, err = XDo.execute("#{XDOTOOL} type #{w_id.nonzero? ? "--window #{w_id.to_i} " : ""}'#{str}'") |
| 334 | + warn(err) unless err.empty? |
| 335 | nil |
| 336 | end |
| 337 | |
| 338 | @@ -224,10 +222,8 @@ |
| 339 | # XDo::Keyboard.char("A") #=> A |
| 340 | # XDo::Keyboard.char("ctrl+c") |
| 341 | def char(c, w_id = 0) |
| 342 | - Open3.popen3("#{XDOTOOL} key #{w_id.nonzero? ? "--window #{w_id.to_i} " : ""}#{c}") do |stdin, stdout, stderr| |
| 343 | - stdin.close_write |
| 344 | - raise(XDo::XError, "Invalid character '#{c}'!") if stderr.read =~ /No such key name/ |
| 345 | - end |
| 346 | + return_code, out, err = XDo.execute("#{XDOTOOL} key #{w_id.nonzero? ? "--window #{w_id.to_i} " : ""}#{c}") |
| 347 | + raise(XDo::XError, "Invalid character '#{c}'!") if err =~ /No such key name/ |
| 348 | c |
| 349 | end |
| 350 | alias key char |
| 351 | @@ -247,10 +243,8 @@ |
| 352 | #===Remarks |
| 353 | #You should release the key sometime via Keyboard.key_up. |
| 354 | def key_down(key, w_id = 0) |
| 355 | - Open3.popen3("#{XDOTOOL} keydown #{w_id.nonzero? ? "--window #{w_id.to_i} " : "" }#{check_for_special_key(key)}") do |stdin, stdout, stderr| |
| 356 | - stdin.close_write |
| 357 | - raise(XDo::XError, "Invalid character '#{key}'!") if stderr.read =~ /No such key name/ |
| 358 | - end |
| 359 | + return_code, out, err = XDo.execute("#{XDOTOOL} keydown #{w_id.nonzero? ? "--window #{w_id.to_i} " : "" }#{check_for_special_key(key)}") |
| 360 | + raise(XDo::XError, "Invalid character '#{key}'!") if err =~ /No such key name/ |
| 361 | key |
| 362 | end |
| 363 | |
| 364 | @@ -269,10 +263,8 @@ |
| 365 | #===Remarks |
| 366 | #This has no effect on already released keys. |
| 367 | def key_up(key, w_id = 0) |
| 368 | - Open3.popen3("#{XDOTOOL} keyup #{w_id.nonzero? ? "--window #{w_id.to_i} " : "" }#{check_for_special_key(key)}") do |stdin, stdout, stderr| |
| 369 | - stdin.close_write |
| 370 | - raise(XDo::XError, "Invalid character '#{key}'!") if stderr.read =~ /No such key name/ |
| 371 | - end |
| 372 | + return_code, out, err = XDo.execute("#{XDOTOOL} keyup #{w_id.nonzero? ? "--window #{w_id.to_i} " : "" }#{check_for_special_key(key)}") |
| 373 | + raise(XDo::XError, "Invalid character '#{key}'!") if err =~ /No such key name/ |
| 374 | key |
| 375 | end |
| 376 | |
| 377 | |
| 378 | === modified file 'tests/misc/lib/xdo/mouse.rb' |
| 379 | --- tests/misc/lib/xdo/mouse.rb 2011-12-07 18:39:44 +0000 |
| 380 | +++ tests/misc/lib/xdo/mouse.rb 2012-01-24 11:51:24 +0000 |
| 381 | @@ -42,7 +42,8 @@ |
| 382 | #===Example |
| 383 | # p XDo::Mouse.position #=> [12, 326] |
| 384 | def position |
| 385 | - out = `#{XDOTOOL} getmouselocation`.match(/x:(\d+) y:(\d+)/) |
| 386 | + return_code, out, err = XDo.execute("#{XDOTOOL} getmouselocation") |
| 387 | + out.match(/x:(\d+) y:(\d+)/) |
| 388 | [$1.to_i, $2.to_i] |
| 389 | end |
| 390 | |
| 391 | @@ -68,7 +69,7 @@ |
| 392 | if set |
| 393 | opts = [] |
| 394 | opts << "--sync" if sync |
| 395 | - `#{XDOTOOL} mousemove #{opts.join(" ")} #{x} #{y}` |
| 396 | + XDo.execute("#{XDOTOOL} mousemove #{opts.join(' ')} #{x} #{y}") |
| 397 | return [x, y] |
| 398 | else |
| 399 | raise(ArgumentError, "speed has to be > 0 (default is 2), was #{speed}!") if speed <= 0 |
| 400 | @@ -140,7 +141,7 @@ |
| 401 | if x and y |
| 402 | move(x, y, speed, set) |
| 403 | end |
| 404 | - `#{XDOTOOL} click #{BUTTON2XDOTOOL[button]}` |
| 405 | + XDo.execute("#{XDOTOOL} click #{BUTTON2XDOTOOL[button]}") |
| 406 | end |
| 407 | |
| 408 | #Scroll with the mouse wheel. +amount+ is the time of steps to scroll. |
| 409 | @@ -188,7 +189,7 @@ |
| 410 | warn("#{caller.first}: Deprecation warning: Use symbols such as :left for the button parameter.") |
| 411 | button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based |
| 412 | end |
| 413 | - `#{XDOTOOL} mousedown #{BUTTON2XDOTOOL[button]}` |
| 414 | + XDo.execute("#{XDOTOOL} mousedown #{BUTTON2XDOTOOL[button]}") |
| 415 | end |
| 416 | |
| 417 | #Releases a mouse button. Probably it's a good idea to call #down first? |
| 418 | @@ -210,7 +211,7 @@ |
| 419 | warn("#{caller.first}: Deprecation warning: Use symbols such as :left for the button parameter.") |
| 420 | button = BUTTON2XDOTOOL.keys[button - 1] #indices are 0-based |
| 421 | end |
| 422 | - `#{XDOTOOL} mouseup #{BUTTON2XDOTOOL[button]}` |
| 423 | + XDo.execute("#{XDOTOOL} mouseup #{BUTTON2XDOTOOL[button]}") |
| 424 | end |
| 425 | |
| 426 | #Executs a drag&drop operation. |
| 427 | |
| 428 | === modified file 'tests/misc/lib/xdo/test/test_keyboard.rb' |
| 429 | --- tests/misc/lib/xdo/test/test_keyboard.rb 2011-12-12 23:02:21 +0000 |
| 430 | +++ tests/misc/lib/xdo/test/test_keyboard.rb 2012-01-24 11:51:24 +0000 |
| 431 | @@ -77,6 +77,7 @@ |
| 432 | assert_equal(TESTTEXT2, XDo::Clipboard.read_clipboard) |
| 433 | end |
| 434 | |
| 435 | +=begin #Disabling this failing test due to poor window Xid detection by xdotool - fix on the way |
| 436 | def test_window_id |
| 437 | XDo::XWindow.unfocus #Ensure that the editor hasn't the input focus anymore |
| 438 | sleep 1 |
| 439 | @@ -90,6 +91,7 @@ |
| 440 | sleep 0.2 |
| 441 | assert_equal(TESTTEXT_SPECIAL.gsub("{TAB}", "\t"), XDo::Clipboard.read_clipboard) |
| 442 | end |
| 443 | +=end |
| 444 | |
| 445 | def test_include |
| 446 | String.class_eval do |
| 447 | |
| 448 | === added file 'tests/misc/lib/xdo/test/test_tdriver_clipboard.rb' |
| 449 | --- tests/misc/lib/xdo/test/test_tdriver_clipboard.rb 1970-01-01 00:00:00 +0000 |
| 450 | +++ tests/misc/lib/xdo/test/test_tdriver_clipboard.rb 2012-01-24 11:51:24 +0000 |
| 451 | @@ -0,0 +1,56 @@ |
| 452 | +#!/usr/bin/env ruby |
| 453 | +#Encoding: UTF-8 |
| 454 | + |
| 455 | +require "test/unit" |
| 456 | + |
| 457 | +# Run XDo test suite through Testability Driver |
| 458 | +require "tdriver" |
| 459 | +require File.join(File.dirname(__FILE__), '../clipboard') |
| 460 | + |
| 461 | +class ClipboardTest < Test::Unit::TestCase |
| 462 | + |
| 463 | + def setup |
| 464 | + XDo.sut = TDriver.sut(:Id => "sut_qt") # the magic to use tdriver |
| 465 | + end |
| 466 | + |
| 467 | + def test_read |
| 468 | + #Write something to the clipboard first |
| 469 | + XDo.execute("echo -n 'Some primary test text' | xsel -i") |
| 470 | + XDo.execute("echo -n 'Some clipboard \ntest text' | xsel -b -i") |
| 471 | + XDo.execute("echo -n 'Some secondary test text' | xsel -s -i") |
| 472 | + |
| 473 | + clip = XDo::Clipboard.read |
| 474 | + assert_equal("Some primary test text", XDo::Clipboard.read_primary) |
| 475 | + assert_equal(XDo::Clipboard.read_primary, clip[:primary]) |
| 476 | + assert_equal("Some clipboard \ntest text", XDo::Clipboard.read_clipboard) |
| 477 | + assert_equal(XDo::Clipboard.read_clipboard, clip[:clipboard]) |
| 478 | + assert_equal("Some secondary test text", XDo::Clipboard.read_secondary) |
| 479 | + assert_equal(XDo::Clipboard.read_secondary, clip[:secondary]) |
| 480 | + end |
| 481 | + |
| 482 | + def test_write |
| 483 | + XDo::Clipboard.write_primary "Primary!" |
| 484 | + XDo::Clipboard.write_clipboard "Clipboard!\nNewline" |
| 485 | + XDo::Clipboard.write_secondary "Secondary!" |
| 486 | + |
| 487 | + assert_equal("Primary!", XDo.sut.execute_shell_command("#{XDo::XSEL}")) |
| 488 | + assert_equal("Secondary!", XDo.sut.execute_shell_command("#{XDo::XSEL} -s")) |
| 489 | + assert_equal("Clipboard!\nNewline", XDo.sut.execute_shell_command("#{XDo::XSEL} -b")) |
| 490 | + |
| 491 | + XDo::Clipboard.write("XYZ", :primary, :secondary, :clipboard) |
| 492 | + assert_equal({ :primary => "XYZ", :secondary => "XYZ", :clipboard => "XYZ"}, XDo::Clipboard.read) |
| 493 | + end |
| 494 | + |
| 495 | + def test_append |
| 496 | + ["primary", "secondary", "clipboard"].each{|m| XDo::Clipboard.send(:"write_#{m}", "This is... ")} |
| 497 | + XDo::Clipboard.append("a Test!", :primary, :secondary, :clipboard) |
| 498 | + ["primary", "secondary", "clipboard"].each{|m| assert_equal(XDo::Clipboard.send(:"read_#{m}"), "This is... a Test!")} |
| 499 | + end |
| 500 | + |
| 501 | + def test_clear |
| 502 | + XDo::Clipboard.write("ABC", :primary, :secondary, :clipboard) |
| 503 | + ["primary", "secondary", "clipboard"].each{|m| XDo::Clipboard.send("clear_#{m}")} |
| 504 | + ["primary", "secondary", "clipboard"].each{|m| assert_equal("", XDo::Clipboard.send("read_#{m}"))} |
| 505 | + end |
| 506 | + |
| 507 | +end |
| 508 | |
| 509 | === added file 'tests/misc/lib/xdo/test/test_tdriver_keyboard.rb' |
| 510 | --- tests/misc/lib/xdo/test/test_tdriver_keyboard.rb 1970-01-01 00:00:00 +0000 |
| 511 | +++ tests/misc/lib/xdo/test/test_tdriver_keyboard.rb 2012-01-24 11:51:24 +0000 |
| 512 | @@ -0,0 +1,130 @@ |
| 513 | +#!/usr/bin/env ruby |
| 514 | +#Encoding: UTF-8 |
| 515 | + |
| 516 | +require "test/unit" |
| 517 | + |
| 518 | +# Run XDo test suite through Testability Driver |
| 519 | +require "tdriver" |
| 520 | +require "timeout" |
| 521 | +require "tempfile" |
| 522 | +require File.join(File.dirname(__FILE__), '../keyboard') |
| 523 | +require File.join(File.dirname(__FILE__), '../clipboard') |
| 524 | +require File.join(File.dirname(__FILE__), '../xwindow') |
| 525 | +require File.join(File.dirname(__FILE__), '../simulatable') |
| 526 | + |
| 527 | +class TestKeyboard < Test::Unit::TestCase |
| 528 | + |
| 529 | + #Command to start a simple text editor |
| 530 | + EDITOR_CMD = "gedit -s" |
| 531 | + EDITOR_NAME = "gedit" |
| 532 | + |
| 533 | + TESTTEXT = "This is test\ntext." |
| 534 | + TESTTEXT2 = "WXY" |
| 535 | + TESTTEXT_RAW = "ä{TAB}?b" |
| 536 | + TESTTEXT_SPECIAL = "ab{TAB}c{TAB}{TAB}d" |
| 537 | + |
| 538 | + def setup |
| 539 | + window_id = -1 |
| 540 | + XDo.sut = TDriver.sut(:Id => "sut_qt") # the magic to use tdriver |
| 541 | + XDo.sut.execute_shell_command(EDITOR_CMD, :detached => true) |
| 542 | + Timeout.timeout(10){ window_id = XDo::XWindow.wait_for_window(EDITOR_NAME)} |
| 543 | + Kernel.raise(SystemCallError, "Unable to open or find #{EDITOR_NAME}") if window_id == -1 |
| 544 | + @xid = XDo::XWindow.new(window_id) |
| 545 | + sleep 1 |
| 546 | + end |
| 547 | + |
| 548 | + def teardown |
| 549 | + @xid.close! |
| 550 | + end |
| 551 | + |
| 552 | + def test_char |
| 553 | + window_id = -1 |
| 554 | + @xid.close! |
| 555 | + #Special file need to be opened |
| 556 | + tempfile = Tempfile.open("XDOTEST") |
| 557 | + tempfile.write(TESTTEXT) |
| 558 | + tempfile.flush |
| 559 | + sleep 3 #Wait for the buffer to be written out |
| 560 | + #Copy file to SUT |
| 561 | + tempfilename = File.basename(tempfile.path) |
| 562 | + XDo.sut.copy_to_sut(:file => tempfile.path, :to => '/var/tmp') |
| 563 | + XDo.execute(EDITOR_CMD + ' /var/tmp/' + tempfilename) |
| 564 | + Timeout.timeout(30){ window_id = XDo::XWindow.wait_for_window(EDITOR_NAME)} |
| 565 | + Kernel.raise(SystemCallError, "Unable to open or find #{EDITOR_NAME}") if window_id == -1 |
| 566 | + @xid = XDo::XWindow.new(window_id) |
| 567 | + sleep 1 |
| 568 | + 20.times{XDo::Keyboard.char("Shift+Right")} |
| 569 | + XDo::Keyboard.ctrl_c |
| 570 | + sleep 0.2 |
| 571 | + assert_equal(TESTTEXT, XDo::Clipboard.read_clipboard) |
| 572 | + tempfile.close |
| 573 | + XDo.execute('rm /var/tmp/' + tempfilename) |
| 574 | + end |
| 575 | + |
| 576 | + def test_simulate |
| 577 | + XDo::Keyboard.simulate("A{BS}#{TESTTEXT2}") |
| 578 | + XDo::Keyboard.ctrl_a |
| 579 | + XDo::Keyboard.ctrl_c |
| 580 | + sleep 0.2 |
| 581 | + assert_equal(TESTTEXT2, XDo::Clipboard.read_clipboard) |
| 582 | + |
| 583 | + XDo::Keyboard.ctrl_a |
| 584 | + XDo::Keyboard.delete |
| 585 | + XDo::Keyboard.simulate(TESTTEXT_SPECIAL) |
| 586 | + XDo::Keyboard.ctrl_a |
| 587 | + XDo::Keyboard.ctrl_c |
| 588 | + sleep 0.2 |
| 589 | + assert_equal(TESTTEXT_SPECIAL.gsub("{TAB}", "\t"), XDo::Clipboard.read_clipboard) |
| 590 | + |
| 591 | + XDo::Keyboard.ctrl_a |
| 592 | + XDo::Keyboard.delete |
| 593 | + XDo::Keyboard.simulate(TESTTEXT_RAW, true) |
| 594 | + XDo::Keyboard.ctrl_a |
| 595 | + XDo::Keyboard.ctrl_c |
| 596 | + sleep 0.2 |
| 597 | + assert_equal(TESTTEXT_RAW, XDo::Clipboard.read_clipboard) |
| 598 | + end |
| 599 | + |
| 600 | + def test_type |
| 601 | + XDo::Keyboard.type(TESTTEXT2) |
| 602 | + XDo::Keyboard.ctrl_a |
| 603 | + XDo::Keyboard.ctrl_c |
| 604 | + sleep 0.2 |
| 605 | + assert_equal(TESTTEXT2, XDo::Clipboard.read_clipboard) |
| 606 | + end |
| 607 | + |
| 608 | +=begin #Disabling this failing test due to poor window Xid detection by xdotool - fix on the way |
| 609 | + def test_window_id |
| 610 | + @xid.unfocus #Ensure that the editor hasn't the input focus anymore |
| 611 | + sleep 1 |
| 612 | + XDo::Keyboard.simulate(TESTTEXT_SPECIAL, false, @xid) |
| 613 | + sleep 1 |
| 614 | + @xid.activate |
| 615 | + XDo::Keyboard.ctrl_a |
| 616 | + XDo::Keyboard.ctrl_c |
| 617 | + sleep 0.2 |
| 618 | + assert_equal(TESTTEXT_SPECIAL.gsub("{TAB}", "\t"), XDo::Clipboard.read_clipboard) |
| 619 | + end |
| 620 | +=end |
| 621 | + |
| 622 | + def test_include |
| 623 | + String.class_eval do |
| 624 | + include XDo::Simulatable |
| 625 | + |
| 626 | + def to_xdo |
| 627 | + to_s |
| 628 | + end |
| 629 | + end |
| 630 | + |
| 631 | + XDo::Keyboard.ctrl_a |
| 632 | + XDo::Keyboard.delete |
| 633 | + "Ein String".simulate |
| 634 | + XDo::Keyboard.ctrl_a |
| 635 | + sleep 0.2 |
| 636 | + XDo::Keyboard.ctrl_c |
| 637 | + sleep 0.2 |
| 638 | + clip = XDo::Clipboard.read_clipboard |
| 639 | + assert_equal("Ein String", clip, "Simulated typed string fails to match") |
| 640 | + end |
| 641 | + |
| 642 | +end |
| 643 | |
| 644 | === added file 'tests/misc/lib/xdo/test/test_tdriver_mouse.rb' |
| 645 | --- tests/misc/lib/xdo/test/test_tdriver_mouse.rb 1970-01-01 00:00:00 +0000 |
| 646 | +++ tests/misc/lib/xdo/test/test_tdriver_mouse.rb 2012-01-24 11:51:24 +0000 |
| 647 | @@ -0,0 +1,36 @@ |
| 648 | +#!/usr/bin/env ruby |
| 649 | +#Encoding: UTF-8 |
| 650 | + |
| 651 | +require "test/unit" |
| 652 | + |
| 653 | +# Run XDo test suite through Testability Driver |
| 654 | +require "tdriver" |
| 655 | +require File.join(File.dirname(__FILE__), '../mouse') |
| 656 | + |
| 657 | +class MouseTest < Test::Unit::TestCase |
| 658 | + |
| 659 | + def setup |
| 660 | + XDo.sut = TDriver.sut(:Id => "sut_qt") # the magic to use tdriver |
| 661 | + end |
| 662 | + |
| 663 | + def test_position |
| 664 | + str = XDo.sut.execute_shell_command("#{XDo::XDOTOOL} getmouselocation") |
| 665 | + xdpos = [] |
| 666 | + xdpos << str.match(/x:\s?(\d+)/)[1] |
| 667 | + xdpos << str.match(/y:\s?(\d+)/)[1] |
| 668 | + xdpos.collect!{|o| o.to_i} |
| 669 | + assert_equal(xdpos, XDo::Mouse.position) |
| 670 | + end |
| 671 | + |
| 672 | + def test_move |
| 673 | + XDo::Mouse.move(200, 200) |
| 674 | + assert_equal([200, 200], XDo::Mouse.position) |
| 675 | + XDo::Mouse.move(0, 0) |
| 676 | + assert_equal([0, 0], XDo::Mouse.position) |
| 677 | + XDo::Mouse.move(0, 200) |
| 678 | + assert_equal([0, 200], XDo::Mouse.position) |
| 679 | + XDo::Mouse.move(100, 100) |
| 680 | + assert_equal([100, 100], XDo::Mouse.position) |
| 681 | + end |
| 682 | + |
| 683 | +end |
| 684 | |
| 685 | === added file 'tests/misc/lib/xdo/test/test_tdriver_xwindow.rb' |
| 686 | --- tests/misc/lib/xdo/test/test_tdriver_xwindow.rb 1970-01-01 00:00:00 +0000 |
| 687 | +++ tests/misc/lib/xdo/test/test_tdriver_xwindow.rb 2012-01-24 11:51:24 +0000 |
| 688 | @@ -0,0 +1,105 @@ |
| 689 | +#!/usr/bin/env ruby |
| 690 | +#Encoding: UTF-8 |
| 691 | + |
| 692 | +require "test/unit" |
| 693 | + |
| 694 | +# Run XDo test suite through Testability Driver |
| 695 | +require "tdriver" |
| 696 | +require File.join(File.dirname(__FILE__), '../xwindow') |
| 697 | +require File.join(File.dirname(__FILE__), '../keyboard') |
| 698 | + |
| 699 | +class WindowTest < Test::Unit::TestCase |
| 700 | + |
| 701 | + attr_accessor :xwindow |
| 702 | + |
| 703 | + #The command used to create new windows. |
| 704 | + #The program MUST NOT start maximized. xdotool has no possibility of |
| 705 | + #acting on maximized windows. |
| 706 | + NEW_WINDOW_NAME = "Home" |
| 707 | + NEW_WINDOW_CMD = "nautilus" |
| 708 | + |
| 709 | + @@xwin = nil |
| 710 | + |
| 711 | + def setup |
| 712 | + XDo.sut = TDriver.sut(:Id => "sut_qt") # the magic to use tdriver |
| 713 | + XDo.execute(NEW_WINDOW_CMD) |
| 714 | + XDo::XWindow.wait_for_window(Regexp.new(Regexp.escape(NEW_WINDOW_NAME))) |
| 715 | + @@xwin = XDo::XWindow.from_title(Regexp.new(Regexp.escape(NEW_WINDOW_NAME))) |
| 716 | + end |
| 717 | + |
| 718 | + def teardown |
| 719 | + @@xwin.focus |
| 720 | + @@xwin.close! |
| 721 | +# Process.kill 'TERM', @editor_pipe.pid |
| 722 | +# @editor_pipe.close |
| 723 | + end |
| 724 | + |
| 725 | + def test_ewmh_active_window |
| 726 | + begin |
| 727 | + XDo::XWindow.from_active |
| 728 | + rescue XDo::XError |
| 729 | + #Standard not available |
| 730 | + notify $!.message |
| 731 | + end |
| 732 | + end |
| 733 | + |
| 734 | + def test_ewmh_wm_desktop |
| 735 | + begin |
| 736 | + XDo::XWindow.desktop_num |
| 737 | + rescue XDo::XError |
| 738 | + #Standard not available |
| 739 | + notify $!.message |
| 740 | + end |
| 741 | + end |
| 742 | + |
| 743 | + def test_ewmh_current_desktop |
| 744 | + begin |
| 745 | + XDo::XWindow.desktop |
| 746 | + rescue XDo::XError |
| 747 | + #Standard not available |
| 748 | + notify $!.message |
| 749 | + end |
| 750 | + end |
| 751 | + |
| 752 | + def test_exists |
| 753 | + assert_equal(true, XDo::XWindow.exists?(@@xwin.title)) |
| 754 | + end |
| 755 | + |
| 756 | + def test_unfocus |
| 757 | + XDo::XWindow.unfocus |
| 758 | + # assert_not_equal(@@xwin.id, XDo::XWindow.from_focused.id) # not supported by WM |
| 759 | + # assert_raise(XDo::XError){XDo::XWindow.from_active} #Nothing's active anymore |
| 760 | + end |
| 761 | + |
| 762 | + def test_active |
| 763 | + @@xwin.activate |
| 764 | + assert_equal(@@xwin.id, XDo::XWindow.from_active.id) |
| 765 | + end |
| 766 | + |
| 767 | + def test_focused |
| 768 | + @@xwin.unfocus |
| 769 | + @@xwin.focus |
| 770 | + assert_equal(@@xwin.id, XDo::XWindow.from_focused.id) |
| 771 | + end |
| 772 | + |
| 773 | + def test_move |
| 774 | + @@xwin.move(87, 57) |
| 775 | + assert_in_delta(87, 3, @@xwin.abs_position[0]) |
| 776 | + assert_in_delta(57, 3, @@xwin.abs_position[1]) |
| 777 | + # assert_equal(@@xwin.abs_position, @@xwin.rel_position) - why should this succeed? |
| 778 | + end |
| 779 | + |
| 780 | + def test_resize |
| 781 | + @@xwin.resize(500, 500) |
| 782 | + assert_equal([500, 500], @@xwin.size) |
| 783 | + end |
| 784 | + |
| 785 | + def test_map |
| 786 | + @@xwin.unmap |
| 787 | + assert_equal(nil, @@xwin.visible?) |
| 788 | + @@xwin.map |
| 789 | + assert_block("Window is not visible."){@@xwin.visible?.kind_of?(Integer)} |
| 790 | + end |
| 791 | + |
| 792 | +end |
| 793 | + |
| 794 | |
| 795 | === added file 'tests/misc/lib/xdo/test/test_xdo.rb' |
| 796 | --- tests/misc/lib/xdo/test/test_xdo.rb 1970-01-01 00:00:00 +0000 |
| 797 | +++ tests/misc/lib/xdo/test/test_xdo.rb 2012-01-24 11:51:24 +0000 |
| 798 | @@ -0,0 +1,63 @@ |
| 799 | +#!/usr/bin/env ruby |
| 800 | +#Encoding: UTF-8 |
| 801 | + |
| 802 | +require "test/unit" |
| 803 | +# The following line includes the complete tdriver environment |
| 804 | +require 'tdriver' |
| 805 | + |
| 806 | +require File.join(File.dirname(__FILE__), '../_xdo') |
| 807 | + |
| 808 | +class XdoTest < Test::Unit::TestCase |
| 809 | + |
| 810 | +def setup |
| 811 | +end |
| 812 | + |
| 813 | +def teardown |
| 814 | +end |
| 815 | + |
| 816 | +def test_host_success |
| 817 | + return_code, stdout, stderr = XDo.execute('ls /') |
| 818 | + assert return_code == 0 |
| 819 | + assert stderr.empty? |
| 820 | +end |
| 821 | + |
| 822 | +def test_host_fail |
| 823 | + return_code, stdout, stderr = XDo.execute('ls /wrong') |
| 824 | + assert return_code == 2 |
| 825 | + assert stdout.empty? |
| 826 | +end |
| 827 | + |
| 828 | +def test_tdriver_success |
| 829 | + XDo.sut = TDriver.sut(:Id => "sut_qt") |
| 830 | + return_code, stdout, stderr = XDo.execute('ls /') |
| 831 | + assert return_code == 0 |
| 832 | + assert stderr.empty? |
| 833 | + XDo.sut = nil |
| 834 | +end |
| 835 | + |
| 836 | +def test_tdriver_fail |
| 837 | + XDo.sut = TDriver.sut(:Id => "sut_qt") |
| 838 | + return_code, stdout, stderr = XDo.execute('ls /wrong') |
| 839 | + assert return_code == 2 |
| 840 | + assert stdout.empty? |
| 841 | + XDo.sut = nil |
| 842 | +end |
| 843 | + |
| 844 | +def test_host_tdriver_equal |
| 845 | + host_return_code, host_stdout, host_stderr = XDo.execute('ls /wrong') |
| 846 | + XDo.sut = TDriver.sut(:Id => "sut_qt") |
| 847 | + sut_return_code, sut_stdout, sut_stderr = XDo.execute('ls /wrong') |
| 848 | + assert_equal(host_return_code, sut_return_code) |
| 849 | + assert_equal(host_stdout, sut_stdout) |
| 850 | + assert_equal(host_stderr, sut_stderr) |
| 851 | +end |
| 852 | + |
| 853 | +def test_host_tdriver_equal |
| 854 | + host_return_code, host_stdout, host_stderr = XDo.execute('echo "hello"') |
| 855 | + XDo.sut = TDriver.sut(:Id => "sut_qt") |
| 856 | + sut_return_code, sut_stdout, sut_stderr = XDo.execute('echo "hello"') |
| 857 | + assert_equal(host_return_code, sut_return_code) |
| 858 | + assert_equal(host_stdout, sut_stdout) |
| 859 | + assert_equal(host_stderr, sut_stderr) |
| 860 | +end |
| 861 | +end |
| 862 | |
| 863 | === modified file 'tests/misc/lib/xdo/xwindow.rb' |
| 864 | --- tests/misc/lib/xdo/xwindow.rb 2012-01-04 16:09:14 +0000 |
| 865 | +++ tests/misc/lib/xdo/xwindow.rb 2012-01-24 11:51:24 +0000 |
| 866 | @@ -78,10 +78,8 @@ |
| 867 | # p XWindow.id_exits?(29360674) #=> true |
| 868 | # p XWindow.id_exists?(123456) #=> false |
| 869 | def id_exists?(id) |
| 870 | - err = "" |
| 871 | - Open3.popen3("#{XDo::XWININFO} -id #{id}"){|stdin, stdout, stderr| err << stderr.read} |
| 872 | - return false unless err.empty? |
| 873 | - return true |
| 874 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -id #{id}") |
| 875 | + return return_code == 0 |
| 876 | end |
| 877 | |
| 878 | #Waits for a window name to exist. |
| 879 | @@ -178,7 +176,8 @@ |
| 880 | opts.each{|sym| cmd << "--#{sym} "} |
| 881 | cmd << "'" << str << "'" |
| 882 | #Don't handle errors since we want an empty array in case of an error |
| 883 | - Open3.popen3(cmd){|stdin, stdout, stderr| stdin.close_write; stdout.read}.lines.to_a.collect{|l| l.strip.to_i} |
| 884 | + return_code, out, err = XDo.execute(cmd) |
| 885 | + out.lines.to_a.collect{|l| l.strip.to_i} |
| 886 | end |
| 887 | |
| 888 | #Returns the internal ID of the currently focused window. |
| 889 | @@ -194,11 +193,9 @@ |
| 890 | #===Remarks |
| 891 | #This method may find an invisible window, see active_window for a more reliable method. |
| 892 | def focused_window(notice_children = false) |
| 893 | - err = "" |
| 894 | - out = "" |
| 895 | - Open3.popen3("#{XDo::XDOTOOL} getwindowfocus #{notice_children ? "-f" : ""}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 896 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} getwindowfocus #{notice_children ? "-f" : ""}") |
| 897 | raise(XDo::XError, err) unless err.empty? |
| 898 | - return out.to_i |
| 899 | + out.to_i |
| 900 | end |
| 901 | |
| 902 | #Returns the internal ID of the currently focused window. |
| 903 | @@ -213,11 +210,9 @@ |
| 904 | # |
| 905 | #Part of the EWMH standard ACTIVE_WINDOW. |
| 906 | def active_window |
| 907 | - err = "" |
| 908 | - out = "" |
| 909 | - Open3.popen3("#{XDo::XDOTOOL} getactivewindow"){|stdin, stdout, stderr| out = stdout.read; err = stderr.read} |
| 910 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} getactivewindow") |
| 911 | raise(XDo::XError, err) unless err.empty? |
| 912 | - return Integer(out) |
| 913 | + out.to_i |
| 914 | end |
| 915 | |
| 916 | #Set the number of working desktops. |
| 917 | @@ -237,9 +232,8 @@ |
| 918 | # |
| 919 | #Part of the EWMH standard WM_DESKTOP. |
| 920 | def desktop_num=(num) |
| 921 | - err = "" |
| 922 | - Open3.popen3("#{XDo::XDOTOOL} set_num_desktops #{num}"){|stdin, stdout, stderr| err << stderr.read} |
| 923 | - raise(XDo::Error, err) unless err.empty? |
| 924 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} set_num_desktops #{num}") |
| 925 | + raise(XDo::XError, err) unless err.empty? |
| 926 | num |
| 927 | end |
| 928 | |
| 929 | @@ -258,11 +252,9 @@ |
| 930 | # |
| 931 | #Part of the EWMH standard WM_DESKTOP. |
| 932 | def desktop_num |
| 933 | - err = "" |
| 934 | - out = "" |
| 935 | - Open3.popen3("#{XDo::XDOTOOL} get_num_desktops"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 936 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} get_num_desktops") |
| 937 | raise(XDo::XError, err) unless err.empty? |
| 938 | - Integer(out) |
| 939 | + out.to_i |
| 940 | end |
| 941 | |
| 942 | #Change the view to desktop +num+. |
| 943 | @@ -282,8 +274,7 @@ |
| 944 | # |
| 945 | #Part of the EWMH standard CURRENT_DESKTOP. |
| 946 | def desktop=(num) |
| 947 | - err = "" |
| 948 | - Open3.popen3("#{XDo::XDOTOOL} set_desktop #{num}"){|stdin, stdout, stderr| err << stderr.read} |
| 949 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} set_desktop #{num}") |
| 950 | raise(XDo::XError, err) unless err.empty? |
| 951 | num |
| 952 | end |
| 953 | @@ -303,13 +294,26 @@ |
| 954 | # |
| 955 | #Part of the EWMH standard CURRENT_DESKTOP. |
| 956 | def desktop |
| 957 | - err = "" |
| 958 | - out = "" |
| 959 | - Open3.popen3("#{XDo::XDOTOOL} get_desktop"){|stdin, stdout, stderr| out = stdout.read; err = stderr.read} |
| 960 | - raise(XDo::XError, err) unless err.empty? |
| 961 | - Integer(out) |
| 962 | - end |
| 963 | - |
| 964 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} get_desktop") |
| 965 | + raise(XDo::XError, err) unless err.empty? |
| 966 | + out.to_i |
| 967 | + end |
| 968 | + |
| 969 | + #Returns the geometry of the display. |
| 970 | + #===Return value |
| 971 | + #The width and height of the display as an array |
| 972 | + #===Raises |
| 973 | + #[XError] Error invoking +xdotool+. |
| 974 | + #===Example |
| 975 | + # p XDo::XWindow.display_geometry #=> [1280,800] |
| 976 | + # width, height = XDo::XWindow.display_geometry |
| 977 | + #===Remarks |
| 978 | + def display_geometry |
| 979 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} getdisplaygeometry") |
| 980 | + raise(XDo::XError, err) unless err.empty? |
| 981 | + return out.split(/ /).first.to_i, out.split(/ /).last.to_i |
| 982 | + end |
| 983 | + |
| 984 | #Creates a XWindow by calling search with the given parameters. |
| 985 | #The window is created from the first ID found. |
| 986 | #===Parameters |
| 987 | @@ -395,11 +399,9 @@ |
| 988 | #===Example |
| 989 | # p XDo::XWindow.root_id #=> 346 |
| 990 | def root_id |
| 991 | - out = "" |
| 992 | - err = "" |
| 993 | - Open3.popen3("#{XDo::XWININFO} -root"){|stdin, stdout, stderr| out << stdout.read.strip; err << stderr.read.strip} |
| 994 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -root") |
| 995 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 996 | - Integer(out.lines.to_a[0].match(/Window id:(.*?)\(/)[1].strip) |
| 997 | + return out.lines.to_a[0].match(/Window id:(.*?)\(/)[1].strip.to_i |
| 998 | end |
| 999 | |
| 1000 | #Creates a XWindow refering to the root window. |
| 1001 | @@ -520,7 +522,8 @@ |
| 1002 | |
| 1003 | dirs.each do |d| |
| 1004 | key = "#{root}/#{d}/#{name}" |
| 1005 | - Open3.popen3("#{GCONFTOOL} -g #{key}"){|stdin, stdout, stderr| out << stdout.read.strip; err << stderr.read.strip} |
| 1006 | + return_code, out, err = XDo.execute("#{GCONFTOOL} -g #{key}") |
| 1007 | + |
| 1008 | #If key doesn't exist, gconftool prints a message (including the requested key) saying so. |
| 1009 | if err.empty? or !out.empty? |
| 1010 | keystroke = out.to_s |
| 1011 | @@ -626,11 +629,10 @@ |
| 1012 | #===Remarks |
| 1013 | #This has no effect on maximized winwows. |
| 1014 | def resize(width, height, use_hints = false, sync = true) |
| 1015 | - err = "" |
| 1016 | opts = [] |
| 1017 | opts << "--usehints" if use_hints |
| 1018 | opts << "--sync" if sync |
| 1019 | - Open3.popen3("#{XDo::XDOTOOL} windowsize #{opts.join(" ")} #{@id} #{width} #{height}"){|stdin, stdout, stderr| err << stderr.read} |
| 1020 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowsize #{opts.join(" ")} #{@id} #{width} #{height}") |
| 1021 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1022 | end |
| 1023 | |
| 1024 | @@ -648,10 +650,9 @@ |
| 1025 | # xwin.move(100, 100) |
| 1026 | # p xwin.abs_position #=> [101, 101] |
| 1027 | def move(x, y, sync = true) |
| 1028 | - err = "" |
| 1029 | opts = [] |
| 1030 | opts << "--sync" if sync |
| 1031 | - Open3.popen3("#{XDo::XDOTOOL} windowmove #{opts.join(" ")} #{@id} #{x} #{y}"){|stdin, stdout, stderr| err << stderr.read} |
| 1032 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowmove #{opts.join(" ")} #{@id} #{x} #{y}") |
| 1033 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1034 | end |
| 1035 | |
| 1036 | @@ -668,10 +669,9 @@ |
| 1037 | #This method may not work on every window manager. You should use |
| 1038 | ##activate, which is supported by more window managers. |
| 1039 | def focus(sync = true) |
| 1040 | - err = "" |
| 1041 | opts = [] |
| 1042 | opts << "--sync" if sync |
| 1043 | - Open3.popen3("#{XDo::XDOTOOL} windowfocus #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read} |
| 1044 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowfocus #{opts.join(" ")} #{@id}") |
| 1045 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1046 | end |
| 1047 | |
| 1048 | @@ -698,10 +698,9 @@ |
| 1049 | # xwin.unmap #Windows are usually mapped |
| 1050 | # xwin.map |
| 1051 | def map(sync = true) |
| 1052 | - err = "" |
| 1053 | opts = [] |
| 1054 | opts << "--sync" if sync |
| 1055 | - Open3.popen3("#{XDo::XDOTOOL} windowmap #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read} |
| 1056 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowmap #{opts.join(" ")} #{@id}") |
| 1057 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1058 | end |
| 1059 | |
| 1060 | @@ -715,10 +714,9 @@ |
| 1061 | #===Example |
| 1062 | # xwin.unmap |
| 1063 | def unmap(sync = true) |
| 1064 | - err = "" |
| 1065 | opts = [] |
| 1066 | opts << "--sync" if sync |
| 1067 | - Open3.popen3("#{XDo::XDOTOOL} windowunmap #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read} |
| 1068 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowunmap #{opts.join(" ")} #{@id}") |
| 1069 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1070 | end |
| 1071 | |
| 1072 | @@ -731,8 +729,7 @@ |
| 1073 | #===Example |
| 1074 | # xwin.raise |
| 1075 | def raise |
| 1076 | - err = "" |
| 1077 | - Open3.popen3("#{XDo::XDOTOOL} windowraise #{@id}"){|stdin, stdout, stderr| err << stderr.read} |
| 1078 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowraise #{@id}") |
| 1079 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1080 | end |
| 1081 | |
| 1082 | @@ -754,10 +751,9 @@ |
| 1083 | def activate(sync = true) |
| 1084 | tried_focus = false |
| 1085 | begin |
| 1086 | - err = "" |
| 1087 | opts = [] |
| 1088 | opts << "--sync" if sync |
| 1089 | - Open3.popen3("#{XDo::XDOTOOL} windowactivate #{opts.join(" ")} #{@id}"){|stdin, stdout, stderr| err << stderr.read} |
| 1090 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} windowactivate #{opts.join(" ")} #{@id}") |
| 1091 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1092 | rescue XDo::XError |
| 1093 | #If no window is active, xdotool's windowactivate fails, |
| 1094 | @@ -789,8 +785,7 @@ |
| 1095 | # |
| 1096 | #Part of the EWMH standard CURRENT_DESKTOP. |
| 1097 | def desktop=(num) |
| 1098 | - err = "" |
| 1099 | - Open3.popen3("#{XDo::XDOTOOL} set_desktop_for_window #{@id} #{num}"){|stdin, stdout, stderr| err << stderr.read} |
| 1100 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} set_desktop_for_window #{@id} #{num}") |
| 1101 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1102 | end |
| 1103 | |
| 1104 | @@ -809,11 +804,9 @@ |
| 1105 | # |
| 1106 | #Part of the EWMH standard CURRENT_DESKTOP. |
| 1107 | def desktop |
| 1108 | - err = "" |
| 1109 | - out = "" |
| 1110 | - Open3.popen3("#{XDo::XDOTOOL} get_desktop_for_window #{@id}"){|stdin, stdout, stderr| out = stdout.read; err << stderr.read} |
| 1111 | + return_code, out, err = XDo.execute("#{XDo::XDOTOOL} get_desktop_for_window #{@id}") |
| 1112 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1113 | - Integer(out) |
| 1114 | + return out.to_i |
| 1115 | end |
| 1116 | |
| 1117 | #The title of the window or nil if it doesn't have a title. |
| 1118 | @@ -824,14 +817,12 @@ |
| 1119 | #===Example |
| 1120 | # p xwin.title #=> "xwindow.rb SciTE" |
| 1121 | def title |
| 1122 | - err = "" |
| 1123 | - out = "" |
| 1124 | if @id == XWindow.root_id #This is the root window |
| 1125 | return "(the root window)" |
| 1126 | elsif @id.zero? |
| 1127 | return "(NULL window)" |
| 1128 | else |
| 1129 | - Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 1130 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -id #{@id}") |
| 1131 | end |
| 1132 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1133 | title = out.strip.lines.to_a[0].match(/"(.*)"/)[1] rescue Kernel.raise(XDo::XError, "No window with ID #{@id} found!") |
| 1134 | @@ -846,9 +837,7 @@ |
| 1135 | #===Example |
| 1136 | # p xwin.abs_position #=> [0, 51] |
| 1137 | def abs_position |
| 1138 | - out = "" |
| 1139 | - err = "" |
| 1140 | - Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 1141 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -id #{@id}") |
| 1142 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1143 | out = out.strip.lines.to_a |
| 1144 | x = out[2].match(/:\s+(\d+)/)[1] |
| 1145 | @@ -865,9 +854,7 @@ |
| 1146 | #===Example |
| 1147 | # p xwin.rel_position => [0, 51] |
| 1148 | def rel_position |
| 1149 | - out = "" |
| 1150 | - err = "" |
| 1151 | - Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 1152 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -id #{@id}") |
| 1153 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1154 | out = out.strip.lines.to_a |
| 1155 | x = out[4].match(/:\s+(\d+)/)[1] |
| 1156 | @@ -883,9 +870,7 @@ |
| 1157 | #===Example |
| 1158 | # p xwin.size #=> [1280, 948] |
| 1159 | def size |
| 1160 | - out = "" |
| 1161 | - err = "" |
| 1162 | - Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 1163 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -id #{@id}") |
| 1164 | out = out.strip.lines.to_a |
| 1165 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1166 | width = out[6].match(/:\s+(\d+)/)[1] |
| 1167 | @@ -903,9 +888,7 @@ |
| 1168 | # xwin.unmap |
| 1169 | # p xwin.visible? #=> nil |
| 1170 | def visible? |
| 1171 | - err = "" |
| 1172 | - out = "" |
| 1173 | - Open3.popen3("#{XDo::XWININFO} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 1174 | + return_code, out, err = XDo.execute("#{XDo::XWININFO} -id #{@id}") |
| 1175 | out = out.strip |
| 1176 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1177 | return out =~ /IsViewable/ |
| 1178 | @@ -982,9 +965,7 @@ |
| 1179 | #===Example |
| 1180 | # xwin.kill! |
| 1181 | def kill! |
| 1182 | - out = "" |
| 1183 | - err = "" |
| 1184 | - Open3.popen3("#{XDo::XKILL} -id #{@id}"){|stdin, stdout, stderr| out << stdout.read; err << stderr.read} |
| 1185 | + return_code, out, err = XDo.execute("#{XDo::XKILL} -id #{@id}") |
| 1186 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1187 | nil |
| 1188 | end |
| 1189 | @@ -1038,8 +1019,7 @@ |
| 1190 | |
| 1191 | #Calls +xdotool+'s set_window command with the given options. |
| 1192 | def set_window(option, value) |
| 1193 | - err = "" |
| 1194 | - Open3.popen3("#{XDOTOOL} set_window --#{option} '#{value}' #{@id}"){|stdin, stdout, stderr| err << stderr.read} |
| 1195 | + return_code, out, err = XDo.execute("#{XDOTOOL} set_window --#{option} '#{value}' #{@id}") |
| 1196 | Kernel.raise(XDo::XError, err) unless err.empty? |
| 1197 | end |
| 1198 | |
| 1199 | |
| 1200 | === modified file 'tests/run-tests.rb' |
| 1201 | --- tests/run-tests.rb 2012-01-23 21:42:53 +0000 |
| 1202 | +++ tests/run-tests.rb 2012-01-24 11:51:24 +0000 |
| 1203 | @@ -63,6 +63,13 @@ |
| 1204 | # Require unit test framework: This enables execution of test cases and also includes assertions (Test::Unit::Assertions) |
| 1205 | require 'testhelper' |
| 1206 | |
| 1207 | +#Establish connection to qttasserver |
| 1208 | +$SUT = TDriver.sut(:Id => "sut_qt") |
| 1209 | + |
| 1210 | +#Include the XDo common library |
| 1211 | +require $library_path + '/xdo/_xdo' |
| 1212 | +XDo.sut = $SUT |
| 1213 | + |
| 1214 | # Enable reports only with -report switch |
| 1215 | include TDriverReportTestUnit if ARGV.delete('-report') |
| 1216 |


How this changes things:
Based on our workflow right now: not at all
You can still run "ruby run-tests.rb" and the tests will run on your desktop.
How to test this MR:
==Host== tdriver_ parameters. xml and add the line server_ port" value="55536" />
- edit /etc/tdriver/
<parameter name="qttas_
This will make the ruby scripts try to connect to the qttasserver inside the VM
- check out this branch, compile
==Target==
- Run Ubuntu inside a VM.
- Install testability-qttas, xdotool, xsel, x11-utils.
- Set up port-forwarding in VirtualBox like this:
- shutdown the VM
- in the VirtualBox settings manager, click Settings and go into the Network pane
- Click the 'Port Forwarding" button
- Add new entry like so:
Name: "guestqttas"
Protocol: "TCP"
Host IP: ""
Host Port: "55536"
Guest IP: ""
Guest Port: "55535"
- now you can start the VM again (good to turn off screensaver too :) )
- check out this branch ******into the same directory as on the host machine******
- compile it
- start "qttasserver"
==Back to Host==
- go into test directory
- run "ruby run-tests.rb"
You should see the tests play out inside the VM.
I plan to script this system up, so that a developer can run all tests inside the VM, and so their desktop isn't messed up & let them surf the inte^W^ W^W^W^W^ W^W^W do more work.
I'm writing this all up at https:/ /wiki.ubuntu. com/Unity2DTest ability% 3A%20How% 20to%20run% 20Tests% 20with% 20VirtualBox