win32-autogui 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,8 +4,15 @@
4
4
 
5
5
  class Myapp < Autogui::Application
6
6
 
7
- def initialize(name=APPNAME, options = {:title=> "MyApp"})
8
- super name, options
7
+ def initialize(options = {})
8
+ # relative path to app using Windows style path
9
+ @name ="exe\\myapp.exe"
10
+ defaults = {
11
+ :title=> "MyApp -",
12
+ :parameters => '--nosplash',
13
+ :main_window_timeout => 20
14
+ }
15
+ super defaults.merge(options)
9
16
  end
10
17
 
11
18
  def edit_window
@@ -16,22 +23,22 @@ def status_bar
16
23
  main_window.children.find {|w| w.window_class == 'TStatusBar'}
17
24
  end
18
25
 
19
- def dialog_about
20
- Autogui::EnumerateDesktopWindows.new.find do |w|
26
+ def dialog_about(options={})
27
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
21
28
  w.title.match(/About MyApp/) && (w.pid == pid)
22
29
  end
23
30
  end
24
31
 
25
- def message_dialog_confirm
26
- Autogui::EnumerateDesktopWindows.new.find do |w|
32
+ def message_dialog_confirm(options={})
33
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
27
34
  w.title.match(/Confirm/) && (w.pid == pid)
28
35
  end
29
36
  end
30
37
 
31
38
  # Title and class are the same as dialog_overwrite_confirm
32
39
  # Use child windows to differentiate
33
- def dialog_overwrite_confirm
34
- Autogui::EnumerateDesktopWindows.new.find do |w|
40
+ def dialog_overwrite_confirm(options={})
41
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
35
42
  w.title.match(/^Text File Save$/) &&
36
43
  (w.pid == pid) &&
37
44
  (w.window_class == "#32770") &&
@@ -40,8 +47,8 @@ def dialog_overwrite_confirm
40
47
  end
41
48
 
42
49
  # title and class are the same as dialog_overwrite_confirm
43
- def file_save_as_dialog
44
- Autogui::EnumerateDesktopWindows.new.find do |w|
50
+ def file_save_as_dialog(options={})
51
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
45
52
  w.title.match(/Text File Save/) &&
46
53
  (w.pid == pid) &&
47
54
  (w.window_class == "#32770") &&
@@ -49,14 +56,14 @@ def file_save_as_dialog
49
56
  end
50
57
  end
51
58
 
52
- def file_open_dialog
53
- Autogui::EnumerateDesktopWindows.new.find do |w|
59
+ def file_open_dialog(options={})
60
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
54
61
  w.title.match(/Text File Open/) && (w.pid == pid)
55
62
  end
56
63
  end
57
64
 
58
- def error_dialog
59
- Autogui::EnumerateDesktopWindows.new.find do |w|
65
+ def error_dialog(options={})
66
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
60
67
  w.title.match(/^MyApp$/) && (w.pid == pid) && (w.window_class == "#32770")
61
68
  end
62
69
  end
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  include Autogui::Input
4
+ include Autogui::Logging
4
5
 
5
6
  describe "FormAbout" do
6
7
 
@@ -41,10 +42,10 @@
41
42
  @dialog_about.title.should == "About MyApp"
42
43
  end
43
44
 
44
- it "should have an 'Ok' button" do
45
+ it "should have an 'OK' button" do
45
46
  button = @dialog_about.children.find {|w| w.window_class == 'TButton'}
46
47
  button.should_not be_nil
47
- button.text.should match(/Ok/)
48
+ button.text.should match(/^OK$/)
48
49
  end
49
50
 
50
51
  end
@@ -1,22 +1,22 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  include Autogui::Input
4
+ include Autogui::Logging
5
+
6
+ logger.level = Autogui::Logging::DEBUG
4
7
 
5
8
  describe "FormMain" do
6
9
  before(:all) do
7
- @debug = false
8
- @verbose = true
9
10
  @application = Myapp.new
10
11
  FileUtils.rm_rf(current_dir)
11
- puts "FormMain before(:all)" if @debug
12
- puts "application:\n#{@application.inspect}\n" if @debug && @verbose
13
- puts "application.combined_text:\n #{@application.combined_text}\n" if @debug && @verbose
12
+ #logger.debug "FormMain before(:all)"
13
+ #logger.debug "application:\n#{@application.inspect}\n"
14
+ #logger.debug "application.combined_text:\n #{@application.combined_text}\n"
14
15
  end
15
16
  before(:each) do
16
17
  @application = Myapp.new unless @application.running?
17
18
  @application.should be_running
18
19
  @application.set_focus
19
- puts "FormMain before(:each)" if @debug
20
20
  end
21
21
  after(:all) do
22
22
  if @application.running?
@@ -25,14 +25,12 @@
25
25
  @application.close(:wait_for_close => true)
26
26
  @application.should_not be_running
27
27
  end
28
- puts "FormMain after(:all)" if @debug
29
28
  end
30
29
  after(:each) do
31
30
  if @application.running?
32
31
  keystroke(VK_N) if @application.message_dialog_confirm || @application.dialog_overwrite_confirm
33
32
  keystroke(VK_ESCAPE) if @application.error_dialog
34
33
  end
35
- puts "FormMain after(:each)" if @debug
36
34
  end
37
35
 
38
36
  describe "after startup" do
@@ -10,15 +10,22 @@
10
10
  require 'spec/autorun'
11
11
  require 'aruba/api'
12
12
 
13
- # aruba helper, returns full path to files in the aruba tmp folder
13
+ # aruba helpers
14
+ #
15
+ # @return full path to files in the aruba tmp folder
14
16
  def fullpath(filename)
15
17
  path = File.expand_path(File.join(current_dir, filename))
16
- path = `cygpath -w #{path}`.chomp if path.match(/^\/cygdrive/) # cygwin?
18
+ if path.match(/^\/cygdrive/)
19
+ # match /cygdrive/c/path/to and return c:\\path\\to
20
+ path = `cygpath -w #{path}`.chomp
21
+ elsif path.match(/.\:/)
22
+ # match c:/path/to and return c:\\path\\to
23
+ path = path.gsub(/\//, '\\')
24
+ end
17
25
  path
18
26
  end
19
-
20
- # return the contents of "filename" in the aruba tmp folder
21
- def get_file_content(filename)
27
+ # @return the contents of "filename" in the aruba tmp folder
28
+ def get_file_contents(filename)
22
29
  in_current_dir do
23
30
  IO.read(filename)
24
31
  end
data/lib/win32/autogui.rb CHANGED
@@ -19,7 +19,7 @@ def self.version
19
19
  end
20
20
  end
21
21
 
22
- # @return False (nil) or True (Integer)
22
+ # @return (Boolean) false (nil) or True (Integer)
23
23
  def self.win32?
24
24
  RUBY_PLATFORM =~ /mingw|mswin|cygwin/i
25
25
  end
@@ -108,7 +108,7 @@ class Application
108
108
  # @example initialize with logging to file at DEBUG level
109
109
  #
110
110
  # include Autogui::Logging
111
- # app = Application.new :name => "calc", :logger_logfile => 'log/calc.log', :logger.level => Log4r::DEBUG
111
+ # app = Application.new :name => "calc", :logger_logfile => 'log/calc.log', :logger.level => Autogui::Logging::DEBUG
112
112
  #
113
113
  # @example initialize without logging to file and turn it on later
114
114
  #
@@ -122,8 +122,8 @@ class Application
122
122
  # @option options [Number] :parameters command line parameters used by Process.create
123
123
  # @option options [Number] :create_process_timeout (10) timeout in seconds to wait for the create_process to return
124
124
  # @option options [Number] :main_window_timeout (10) timeout in seconds to wait for main_window to appear
125
- # @option options [String] :logger_logfile (nil) initialize Log4r::Logger's output filename
126
- # @option options [String] :logger_level (Log4r::WARN) initialize Log4r::Logger's initial level
125
+ # @option options [String] :logger_logfile (nil) initialize logger's output filename
126
+ # @option options [String] :logger_level (Autogui::Logging::WARN) initialize logger's initial level
127
127
  #
128
128
  def initialize(options = {})
129
129
 
@@ -206,7 +206,7 @@ def main_window
206
206
  w.title.match(title) && w.pid == pid
207
207
  end
208
208
  sleep 0.1
209
- end until @main_window
209
+ end until @main_window
210
210
  end
211
211
 
212
212
  # post sanity checks
@@ -18,9 +18,15 @@ def logfile
18
18
  end
19
19
 
20
20
  def logfile=(fn)
21
- FileOutputter.new(:logfile, :filename => fn, :trunc => true)
22
- Outputter[:logfile].formatter = Log4r::PatternFormatter.new(:pattern => "[%5l %d] %M [%t]")
23
- add(:logfile)
21
+ if fn == nil
22
+ puts "removing log to file"
23
+ remove(:logfile) if @filename
24
+ else
25
+ FileOutputter.new(:logfile, :filename => fn, :trunc => true)
26
+ Outputter[:logfile].formatter = Log4r::PatternFormatter.new(:pattern => "[%5l %d] %M [%t]")
27
+ add(:logfile)
28
+ end
29
+ @filename = fn
24
30
  end
25
31
 
26
32
  end
@@ -28,11 +34,19 @@ def logfile=(fn)
28
34
 
29
35
  module Autogui
30
36
 
31
- STANDARD_LOGGER = 'standard'
32
-
33
37
  # wrapper for Log4r gem
34
38
  module Logging
35
39
 
40
+ # Redefine logging levels so that they can be accessed before
41
+ # the logger is initialized at the expense of flexibility.
42
+ DEBUG = 1
43
+ INFO = 2
44
+ WARN = 3
45
+ ERROR = 4
46
+ FATAL = 5
47
+
48
+ STANDARD_LOGGER = 'standard'
49
+
36
50
  # Logging mixin allows simple logging setup
37
51
  # to STDOUT and optionally, to one filename. Logger is a wrapper
38
52
  # for Log4r::Logger it accepts any methods that
@@ -45,7 +59,7 @@ module Logging
45
59
  # logger.filename = 'log/autogui.log'
46
60
  # logger.warn "warning message goes to 'log/autogui.log'"
47
61
  #
48
- # logger.level = Log4r::DEBUG
62
+ # logger.level = Autogui::Logging::DEBUG
49
63
  # logger.debug "this message goes to 'log/autogui.log'"
50
64
  #
51
65
  def logger
@@ -59,7 +73,17 @@ def logger
59
73
  # Initialize the logger, defaults to log4r::Warn
60
74
  def init_logger
61
75
  log = Log4r::Logger.new(STANDARD_LOGGER)
62
- Log4r::Logger[STANDARD_LOGGER].level = Log4r::WARN
76
+
77
+ # sanity checks since we defined log4r's dynamic levels statically
78
+ unless (Log4r::DEBUG == DEBUG) &&
79
+ (Log4r::INFO == INFO) &&
80
+ (Log4r::WARN == WARN) &&
81
+ (Log4r::ERROR == ERROR) &&
82
+ (Log4r::FATAL == FATAL)
83
+ raise "Logger levels do not match Log4r levels, levels may have been customized"
84
+ end
85
+
86
+ Log4r::Logger[STANDARD_LOGGER].level = WARN
63
87
  Log4r::Logger[STANDARD_LOGGER].trace = true
64
88
 
65
89
  Log4r::StderrOutputter.new :console
@@ -1,10 +1,13 @@
1
1
  require 'timeout'
2
2
  require 'windows/window'
3
3
  require 'windows/window/message'
4
+ require 'windows/window/classes'
4
5
  require 'win32/autogui/windows/window'
5
6
 
6
7
  module Autogui
7
8
 
9
+ class FindTimeout < Timeout::Error; end
10
+
8
11
  # Enumerate desktop child windows
9
12
  #
10
13
  # Start at the desktop and work down through all the child windows
@@ -12,6 +15,35 @@ module Autogui
12
15
  class EnumerateDesktopWindows
13
16
  include Enumerable
14
17
  include Windows::Window
18
+ include Autogui::Logging
19
+
20
+ # @return [Number] timeout (0) in seconds
21
+ attr_accessor :timeout
22
+
23
+ # @option options [Number] :timeout (0) maximum seconds to continue enumerating windows
24
+ def initialize(options ={})
25
+ @timeout = options[:timeout] || 0
26
+ end
27
+
28
+ # redefine Enumerable's find to continue looping until a timeout reached
29
+ def find(ifnone = nil)
30
+ return to_enum :find, ifnone unless block_given?
31
+
32
+ begin
33
+ Timeout.timeout(timeout, FindTimeout) do
34
+ begin
35
+ each { |o| return o if yield(o) }
36
+ sleep 0.2 unless (timeout == 0)
37
+ #logger.debug "find looping" unless (timeout == 0)
38
+ end until (timeout == 0)
39
+ end
40
+ rescue FindTimeout
41
+ logger.warn "EnumerateDesktopWindows.find timeout"
42
+ nil
43
+ end
44
+
45
+ ifnone.call if ifnone
46
+ end
15
47
 
16
48
  def each
17
49
  child_after = 0
@@ -50,6 +82,7 @@ def each
50
82
  class Window
51
83
  include Windows::Window # instance methods from windows-pr gem
52
84
  include Windows::Window::Message # PostMessage and constants
85
+ include Windows::Window::Classes # GetClassName
53
86
  include Autogui::Logging
54
87
  include Autogui::Input
55
88
 
@@ -97,11 +130,11 @@ def wait_for_close(options={})
97
130
  end
98
131
  end
99
132
 
100
- # @return [String] the ANSI Windows ClassName
133
+ # @return [String] the Windows ClassName
101
134
  #
102
135
  def window_class
103
136
  buffer = "\0" * 255
104
- length = GetClassNameA(handle, buffer, buffer.length)
137
+ length = GetClassName(handle, buffer, buffer.length)
105
138
  length == 0 ? '' : buffer[0..length - 1]
106
139
  end
107
140
 
@@ -16,7 +16,6 @@ module Window
16
16
  API.new('IsWindow', 'L', 'I', 'user32')
17
17
  API.new('SetForegroundWindow', 'L', 'I', 'user32')
18
18
  API.new('SendMessageA', 'LIIP', 'I', 'user32')
19
- API.new('GetClassNameA', 'LPI', 'I', 'user32')
20
19
 
21
20
  end
22
21
  end
@@ -8,7 +8,8 @@ class Calculator < Autogui::Application
8
8
  def initialize(options = {})
9
9
  defaults = {
10
10
  :name => "calc",
11
- :title => "Calculator"
11
+ :title => "Calculator",
12
+ :logger_level => Autogui::Logging::DEBUG
12
13
  }
13
14
  super defaults.merge(options)
14
15
  end
@@ -19,12 +20,12 @@ def edit_window
19
20
  end
20
21
 
21
22
  # About dialog, hotkey (VK_MENU, VK_H, VK_A)
22
- def dialog_about
23
- Autogui::EnumerateDesktopWindows.new.find do |w|
23
+ def dialog_about(options = {})
24
+ Autogui::EnumerateDesktopWindows.new(options).find do |w|
24
25
  w.title.match(/About Calculator/) && (w.pid == pid)
25
26
  end
26
27
  end
27
-
28
+
28
29
  # the 'CE' button
29
30
  def clear_entry
30
31
  set_focus
@@ -1,6 +1,7 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  include Autogui::Input
4
+ include Autogui::Logging
4
5
 
5
6
  describe Autogui::Application do
6
7
 
@@ -109,7 +110,7 @@
109
110
  it "should copy the edit window" do
110
111
  @calculator.set_focus
111
112
  type_in("3002")
112
- @calculator.edit_window.text.strip.should == "3,002."
113
+ @calculator.edit_window.text.strip.should match(/3,?002\./)
113
114
  @calculator.edit_window.set_focus
114
115
  keystroke(VK_CONTROL, VK_C)
115
116
  @calculator.clipboard.text.should == "3002"
@@ -122,7 +123,7 @@
122
123
  @calculator.clipboard.text = "12345"
123
124
  @calculator.edit_window.text.strip.should == "0."
124
125
  keystroke(VK_CONTROL, VK_V)
125
- @calculator.edit_window.text.strip.should == "12,345."
126
+ @calculator.edit_window.text.strip.should match(/12,?345\./)
126
127
  end
127
128
  end
128
129
 
@@ -3,6 +3,10 @@
3
3
  include Autogui::Logging
4
4
 
5
5
  describe Autogui::Logging do
6
+ before(:all) do
7
+ # quiet console output, we are only testing the file output
8
+ logger.remove(:console)
9
+ end
6
10
  before(:each) do
7
11
  FileUtils.rm_rf(current_dir)
8
12
  @logfile = "autogui.log"
@@ -13,50 +17,63 @@
13
17
  @application.close(:wait_for_close => true) if @application.running?
14
18
  @application.should_not be_running
15
19
  end
20
+ logger.remove(:logfile)
21
+ end
22
+ after(:all) do
23
+ logger.add(:console)
16
24
  end
17
25
 
18
26
  describe "to file" do
27
+
19
28
  it "should truncate the log on create" do
20
- get_file_content(@logfile).should == 'the quick brown fox'
29
+ get_file_contents(@logfile).should == 'the quick brown fox'
21
30
  @application = Calculator.new :logger_logfile => fullpath(@logfile)
22
- get_file_content(@logfile).should == ''
31
+ get_file_contents(@logfile).should == ''
23
32
  end
24
33
 
25
34
  it "should not log unless 'logger.logfile' is set" do
26
35
  @application = Calculator.new
27
- get_file_content(@logfile).should == 'the quick brown fox'
28
- logger.warn "warning message here"
29
- get_file_content(@logfile).should == 'the quick brown fox'
36
+ get_file_contents(@logfile).should == 'the quick brown fox'
37
+ logger.warn "warning message 0"
38
+ get_file_contents(@logfile).should == 'the quick brown fox'
30
39
  logger.logfile = fullpath(@logfile)
31
- logger.warn "warning message here"
32
- get_file_content(@logfile).should match(/warning message here/)
40
+ logger.warn "warning message 1"
41
+ get_file_contents(@logfile).should match(/warning message 1/)
42
+ logger.logfile = nil
43
+ logger.warn "warning message 2"
44
+ get_file_contents(@logfile).should_not match(/warning message 2/)
33
45
  end
34
46
 
35
47
  it "should log warnings" do
36
48
  @application = Calculator.new :logger_logfile => fullpath(@logfile)
37
- get_file_content(@logfile).should == ''
49
+ get_file_contents(@logfile).should == ''
38
50
  logger.warn "warning message here"
39
- get_file_content(@logfile).should match(/warning message here/)
51
+ get_file_contents(@logfile).should match(/warning message here/)
40
52
  end
41
53
 
42
54
  it "should log application raised exceptions via 'application.raise_error'" do
43
- get_file_content(@logfile).should == 'the quick brown fox'
55
+ get_file_contents(@logfile).should == 'the quick brown fox'
44
56
  begin
45
57
  @application = Calculator.new :logger_logfile => fullpath(@logfile), :name => nil
46
58
  rescue
47
- # expected exception
48
59
  end
49
- get_file_content(@logfile).should match(/application name not set/)
60
+ get_file_contents(@logfile).should match(/application name not set/)
50
61
  end
51
62
 
52
63
  it "should log debug messages when debug level set" do
53
64
  @application = Calculator.new :logger_logfile => fullpath(@logfile)
54
- get_file_content(@logfile).should == ''
55
- logger.debug "debug message here"
56
- get_file_content(@logfile).should_not match(/debug message here/)
57
- logger.level = Log4r::DEBUG
58
- logger.debug "debug message here"
59
- get_file_content(@logfile).should match(/debug message here/)
65
+ level_save = logger.level
66
+ begin
67
+ logger.level = Autogui::Logging::WARN
68
+ get_file_contents(@logfile).should == ''
69
+ logger.debug "debug message here 1"
70
+ get_file_contents(@logfile).should_not match(/debug message here 1/)
71
+ logger.level = Autogui::Logging::DEBUG
72
+ logger.debug "debug message here 2"
73
+ get_file_contents(@logfile).should match(/debug message here 2/)
74
+ ensure
75
+ logger.level = level_save
76
+ end
60
77
  end
61
78
  end
62
79