win32-autogui 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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