win32-autogui 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ rerun.txt
8
8
  tags
9
9
  tmp/fixtures
10
10
  tmp/aruba
11
+ autogui.log
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- win32-autogui (0.2.1)
4
+ win32-autogui (0.3.0)
5
+ log4r (>= 1.1.9)
5
6
  win32-clipboard (>= 0.5.2)
6
7
  win32-process (>= 0.6.2)
7
8
  windows-api (>= 0.4.0)
@@ -26,6 +27,7 @@ GEM
26
27
  json (~> 1.4.6)
27
28
  term-ansicolor (~> 1.0.5)
28
29
  json (1.4.6)
30
+ log4r (1.1.9)
29
31
  rake (0.8.7)
30
32
  rdiscount (1.6.5)
31
33
  rspec (1.3.1)
@@ -49,6 +51,7 @@ DEPENDENCIES
49
51
  aruba (>= 0.2.3)
50
52
  bundler (>= 1.0.3)
51
53
  cucumber (>= 0.9.2)
54
+ log4r (>= 1.1.9)
52
55
  rake (>= 0.8.7)
53
56
  rdiscount (>= 1.6.5)
54
57
  rspec (= 1.3.1)
@@ -6,9 +6,15 @@ Most recent changes are at the top
6
6
  Changes
7
7
  -------
8
8
 
9
+ ### 0.3.0 - 11/08/2010 ###
10
+
11
+ * Added Log4R logging
12
+ * Window.set_focus works first call in IRB
13
+
9
14
  ### 0.2.1 - 11/05/2010 ###
10
15
 
11
16
  * Added missing "require 'timeout'", fixes issue #1
17
+ * Replaced missing '@calculator' tag required to close down the calculator after running cucumber features
12
18
 
13
19
  ### 0.2.0 - 11/04/2010 ###
14
20
 
@@ -46,7 +46,8 @@ The first step is to subclass Win32-autogui's application class.
46
46
  def initialize(options = {})
47
47
  defaults = {
48
48
  :name => "calc",
49
- :title => "Calculator"
49
+ :title => "Calculator",
50
+ :logger_filename => "calculator.log"
50
51
  }
51
52
  super defaults.merge(options)
52
53
  end
@@ -194,6 +195,7 @@ Win32-autogui depends on the following RubyGems
194
195
  * Windows-pr <http://github.com/djberg96/windows-pr>
195
196
  * Win32-process <http://github.com/djberg96/win32-process>
196
197
  * Win32-clipboard <http://github.com/djberg96/win32-clipboard>
198
+ * Log4r for logging <http://log4r.rubyforge.org/>
197
199
 
198
200
 
199
201
  Installation
@@ -7,3 +7,4 @@ general
7
7
  * (1.0) stop changing $LOAD_PATH in specs and features, modify ENV[] in before and afters
8
8
  * (1.0) implement Application.find and add option to not start application automatically, allows driving already running applications
9
9
  * (1.0) application class should be a mixin, not classical inheritance
10
+ * (1.0) "rake release" broken on Windows, create patch to fix Bundler. For now, 'git tag v0.3.0 && rake build && gem push
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -39,6 +39,7 @@ object FormMain: TFormMain
39
39
  Top = 244
40
40
  object ActionFileExit: TAction
41
41
  Caption = 'E&xit'
42
+ ShortCut = 16472
42
43
  OnExecute = ActionFileExitExecute
43
44
  end
44
45
  object ActionHelpAbout: TAction
@@ -271,4 +271,13 @@
271
271
  end
272
272
  end
273
273
 
274
+ describe "hotkey (VK_CONTROL, VK_X)" do
275
+ it "should should exit the program" do
276
+ @application.main_window.is_window?.should == true
277
+ keystroke(VK_CONTROL, VK_X)
278
+ @application.main_window.is_window?.should == false
279
+ @application.should_not be_running
280
+ end
281
+ end
282
+
274
283
  end
@@ -1,4 +1,5 @@
1
1
  # require all files here
2
+ require 'win32/autogui/logging'
2
3
  require 'win32/autogui/input'
3
4
  require 'win32/autogui/window'
4
5
  require 'win32/autogui/application'
@@ -40,7 +40,8 @@ def text=(str)
40
40
  # def initialize(options = {})
41
41
  # defaults = {
42
42
  # :name => "calc",
43
- # :title => "Calculator"
43
+ # :title => "Calculator",
44
+ # :logger_logfile => 'log/calc.log'
44
45
  # }
45
46
  # super defaults.merge(options)
46
47
  # end
@@ -65,6 +66,7 @@ class Application
65
66
  include Windows::Process
66
67
  include Windows::Synchronize
67
68
  include Windows::Handle
69
+ include Autogui::Logging
68
70
 
69
71
  # @return [String] the executable name of the application
70
72
  attr_accessor :name
@@ -89,11 +91,30 @@ class Application
89
91
 
90
92
  # @example initialize an application on the path
91
93
  #
92
- # Application.new :name => "calc"
94
+ # app = Application.new :name => "calc"
95
+ #
96
+ # @example initialize with relative DOS path
97
+ #
98
+ # app = Application.new :name => "binaries\\mybinary.exe"
93
99
  #
94
100
  # @example initialize with full DOS path
95
101
  #
96
- # Application.new :name => "\\windows\\system32\\calc.exe"
102
+ # app = Application.new :name => "\\windows\\system32\\calc.exe"
103
+ #
104
+ # @example initialize with logging to file at the default WARN level (STDOUT logging is the default)
105
+ #
106
+ # app = Application.new :name => "calc", :logger_logfile => 'log/calc.log'
107
+ #
108
+ # @example initialize with logging to file at DEBUG level
109
+ #
110
+ # include Autogui::Logging
111
+ # app = Application.new :name => "calc", :logger_logfile => 'log/calc.log', :logger.level => Log4r::DEBUG
112
+ #
113
+ # @example initialize without logging to file and turn it on later
114
+ #
115
+ # include Autogui::Logging
116
+ # app = Application.new :name => "calc"
117
+ # logger.logfile = 'app.log'
97
118
  #
98
119
  # @param [Hash] options initialize options
99
120
  # @option options [String] :name a valid win32 exe name with optional path
@@ -101,11 +122,13 @@ class Application
101
122
  # @option options [Number] :parameters command line parameters used by Process.create
102
123
  # @option options [Number] :create_process_timeout (10) timeout in seconds to wait for the create_process to return
103
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
104
127
  #
105
128
  def initialize(options = {})
106
129
 
107
130
  unless options.kind_of?(Hash)
108
- raise ArgumentError, 'Initialize expecting options to be a Hash'
131
+ raise_error ArgumentError, 'Initialize expecting options to be a Hash'
109
132
  end
110
133
 
111
134
  @name = options[:name] || name
@@ -114,8 +137,12 @@ def initialize(options = {})
114
137
  @create_process_timeout = options[:create_process_timeout] || 10
115
138
  @parameters = options[:parameters]
116
139
 
140
+ # logger setup
141
+ logger.logfile = options[:logger_logfile] if options[:logger_logfile]
142
+ logger.level = options[:logger_level] if options[:logger_level]
143
+
117
144
  # sanity checks
118
- raise 'Application name not set' unless name
145
+ raise_error 'application name not set' unless name
119
146
 
120
147
  start
121
148
  end
@@ -151,8 +178,8 @@ def start
151
178
  CloseHandle(process_handle)
152
179
  CloseHandle(thread_handle)
153
180
 
154
- raise "Start command failed on create_process_timeout" if ret == WAIT_TIMEOUT
155
- raise "Start command failed while waiting for idle input, reason unknown" unless (ret == 0)
181
+ raise_error "start command failed on create_process_timeout" if ret == WAIT_TIMEOUT
182
+ raise_error "start command failed while waiting for idle input, reason unknown" unless (ret == 0)
156
183
  @pid
157
184
  end
158
185
 
@@ -168,6 +195,10 @@ def start
168
195
  def main_window
169
196
  return @main_window if @main_window
170
197
 
198
+ # pre sanity checks
199
+ raise_error "calling main_window without a pid, application not initialized properly" unless @pid
200
+ raise_error "calling main_window without a window title, application not initialized properly" unless @title
201
+
171
202
  timeout(main_window_timeout) do
172
203
  begin
173
204
  # There may be multiple instances, use title and pid to id our main window
@@ -178,8 +209,8 @@ def main_window
178
209
  end until @main_window
179
210
  end
180
211
 
181
- # sanity checks
182
- raise "cannot find main_window, check application title" unless @main_window
212
+ # post sanity checks
213
+ raise_error "cannot find main_window, check application title" unless @main_window
183
214
 
184
215
  @main_window
185
216
  end
@@ -243,7 +274,30 @@ def clipboard
243
274
  @clipboard || Autogui::Clipboard.new
244
275
  end
245
276
 
246
- private
277
+ private
278
+
279
+ # @overload raise_error(exception, message)
280
+ # raise and log specific exception with message
281
+ # @param [Exception] to raise
282
+ # @param [String] message error message to raise
283
+ #
284
+ # @overload raise_error(message)
285
+ # raise and log generic exception with message
286
+ # @param [String] message error message to raise
287
+ #
288
+ def raise_error(*args)
289
+ if args.first.kind_of?(Exception)
290
+ exception_type = args.shift
291
+ error_message = args.shift || 'Unknown error'
292
+ else
293
+ raise ArgumentError unless args.first.is_a?(String)
294
+ exception_type = RuntimeError
295
+ error_message = args.shift || 'Unknown error'
296
+ end
297
+
298
+ logger.error error_message
299
+ raise exception_type, error_message
300
+ end
247
301
 
248
302
  end
249
303
 
@@ -0,0 +1,71 @@
1
+ require 'log4r'
2
+
3
+ # Open up Log4r and add a simple log accessor for 'logfile'
4
+ #
5
+ # @example allows simple setup
6
+ #
7
+ # include Autogui::Logging
8
+ #
9
+ # logger.filename = 'log/autogui.log'
10
+ # logger.warn "warning message goes to log file"
11
+ #
12
+ module Log4r
13
+ class Logger
14
+
15
+ # @return [String] filename of the logfile
16
+ def logfile
17
+ @filename
18
+ end
19
+
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)
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ module Autogui
30
+
31
+ STANDARD_LOGGER = 'standard'
32
+
33
+ # wrapper for Log4r gem
34
+ module Logging
35
+
36
+ # Logging mixin allows simple logging setup
37
+ # to STDOUT and optionally, to one filename. Logger is a wrapper
38
+ # for Log4r::Logger it accepts any methods that
39
+ # Log4r::Logger accepts in addition to the "logfile" filename.
40
+ #
41
+ # @example simple logging to file setup
42
+ #
43
+ # include Autogui::Logging
44
+ #
45
+ # logger.filename = 'log/autogui.log'
46
+ # logger.warn "warning message goes to 'log/autogui.log'"
47
+ #
48
+ # logger.level = Log4r::DEBUG
49
+ # logger.debug "this message goes to 'log/autogui.log'"
50
+ #
51
+ def logger
52
+ init_logger if Log4r::Logger[STANDARD_LOGGER].nil?
53
+ Log4r::Logger[STANDARD_LOGGER]
54
+ end
55
+
56
+
57
+ protected
58
+
59
+ # Initialize the logger, defaults to log4r::Warn
60
+ def init_logger
61
+ log = Log4r::Logger.new(STANDARD_LOGGER)
62
+ Log4r::Logger[STANDARD_LOGGER].level = Log4r::WARN
63
+ Log4r::Logger[STANDARD_LOGGER].trace = true
64
+
65
+ Log4r::StderrOutputter.new :console
66
+ Log4r::Outputter[:console].formatter = Log4r::PatternFormatter.new(:pattern => "[%l] %m")
67
+ log.add(:console)
68
+ end
69
+
70
+ end
71
+ end
@@ -50,6 +50,8 @@ def each
50
50
  class Window
51
51
  include Windows::Window # instance methods from windows-pr gem
52
52
  include Windows::Window::Message # PostMessage and constants
53
+ include Autogui::Logging
54
+ include Autogui::Input
53
55
 
54
56
  attr_reader :handle
55
57
 
@@ -82,7 +84,7 @@ def close(options={})
82
84
  PostMessage(handle, WM_SYSCOMMAND, SC_CLOSE, 0)
83
85
  wait_for_close(options) if (options[:wait_for_close] == true)
84
86
  end
85
-
87
+
86
88
  # Wait for the window to close
87
89
  #
88
90
  # @param [Hash] options
@@ -96,7 +98,7 @@ def wait_for_close(options={})
96
98
  end
97
99
 
98
100
  # @return [String] the ANSI Windows ClassName
99
- #
101
+ #
100
102
  def window_class
101
103
  buffer = "\0" * 255
102
104
  length = GetClassNameA(handle, buffer, buffer.length)
@@ -125,13 +127,31 @@ def is_window?
125
127
  end
126
128
 
127
129
  # Brings the window into the foreground and activates it.
128
- # Keyboard input is directed to the window, and various visual cues
130
+ # Keyboard input is directed to the window, and various visual cues
129
131
  # are changed for the user.
130
132
  #
133
+ # A process can set the foreground window only if one of the following conditions is true:
134
+ #
135
+ # * The process is the foreground process.
136
+ # * The process was started by the foreground process.
137
+ # * The process received the last input event.
138
+ # * There is no foreground process.
139
+ # * The foreground process is being debugged.
140
+ # * The foreground is not locked.
141
+ # * The foreground lock time-out has expired.
142
+ # * No menus are active.
143
+ #
131
144
  # @return [Number] nonzero number if sucessful, nil or zero if failed
132
145
  #
133
146
  def set_focus
134
- SetForegroundWindow(handle) if is_window?
147
+ if is_window?
148
+ # if current process was the last to receive input, we can be sure that
149
+ # SetForegroundWindow will be allowed. Send the shift key to whatever has
150
+ # the focus now. This allows IRB to set_focus.
151
+ keystroke(VK_SHIFT)
152
+ ret = SetForegroundWindow(handle)
153
+ logger.warn("SetForegroundWindow failed") if ret == 0
154
+ end
135
155
  end
136
156
 
137
157
  # The identifier (pid) of the process that created the window
@@ -0,0 +1,63 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ include Autogui::Logging
4
+
5
+ describe Autogui::Logging do
6
+ before(:each) do
7
+ FileUtils.rm_rf(current_dir)
8
+ @logfile = "autogui.log"
9
+ create_file(@logfile, "the quick brown fox")
10
+ end
11
+ after(:each) do
12
+ if @application
13
+ @application.close(:wait_for_close => true) if @application.running?
14
+ @application.should_not be_running
15
+ end
16
+ end
17
+
18
+ describe "to file" do
19
+ it "should truncate the log on create" do
20
+ get_file_content(@logfile).should == 'the quick brown fox'
21
+ @application = Calculator.new :logger_logfile => fullpath(@logfile)
22
+ get_file_content(@logfile).should == ''
23
+ end
24
+
25
+ it "should not log unless 'logger.logfile' is set" do
26
+ @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'
30
+ logger.logfile = fullpath(@logfile)
31
+ logger.warn "warning message here"
32
+ get_file_content(@logfile).should match(/warning message here/)
33
+ end
34
+
35
+ it "should log warnings" do
36
+ @application = Calculator.new :logger_logfile => fullpath(@logfile)
37
+ get_file_content(@logfile).should == ''
38
+ logger.warn "warning message here"
39
+ get_file_content(@logfile).should match(/warning message here/)
40
+ end
41
+
42
+ it "should log application raised exceptions via 'application.raise_error'" do
43
+ get_file_content(@logfile).should == 'the quick brown fox'
44
+ begin
45
+ @application = Calculator.new :logger_logfile => fullpath(@logfile), :name => nil
46
+ rescue
47
+ # expected exception
48
+ end
49
+ get_file_content(@logfile).should match(/application name not set/)
50
+ end
51
+
52
+ it "should log debug messages when debug level set" do
53
+ @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/)
60
+ end
61
+ end
62
+
63
+ end
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency "windows-pr", ">= 1.0.9"
20
20
  s.add_dependency "win32-process", ">= 0.6.2"
21
21
  s.add_dependency "win32-clipboard", ">= 0.5.2"
22
+ s.add_dependency "log4r", ">= 1.1.9"
22
23
 
23
24
  s.add_development_dependency "bundler", ">= 1.0.3"
24
25
  s.add_development_dependency "rspec", "= 1.3.1"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-autogui
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Robert Wahler
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-05 00:00:00 -04:00
18
+ date: 2010-11-08 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -84,6 +84,22 @@ dependencies:
84
84
  prerelease: false
85
85
  - !ruby/object:Gem::Dependency
86
86
  version_requirements: &id005 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 1
92
+ segments:
93
+ - 1
94
+ - 1
95
+ - 9
96
+ version: 1.1.9
97
+ requirement: *id005
98
+ type: :runtime
99
+ name: log4r
100
+ prerelease: false
101
+ - !ruby/object:Gem::Dependency
102
+ version_requirements: &id006 !ruby/object:Gem::Requirement
87
103
  none: false
88
104
  requirements:
89
105
  - - ">="
@@ -94,12 +110,12 @@ dependencies:
94
110
  - 0
95
111
  - 3
96
112
  version: 1.0.3
97
- requirement: *id005
113
+ requirement: *id006
98
114
  type: :development
99
115
  name: bundler
100
116
  prerelease: false
101
117
  - !ruby/object:Gem::Dependency
102
- version_requirements: &id006 !ruby/object:Gem::Requirement
118
+ version_requirements: &id007 !ruby/object:Gem::Requirement
103
119
  none: false
104
120
  requirements:
105
121
  - - "="
@@ -110,12 +126,12 @@ dependencies:
110
126
  - 3
111
127
  - 1
112
128
  version: 1.3.1
113
- requirement: *id006
129
+ requirement: *id007
114
130
  type: :development
115
131
  name: rspec
116
132
  prerelease: false
117
133
  - !ruby/object:Gem::Dependency
118
- version_requirements: &id007 !ruby/object:Gem::Requirement
134
+ version_requirements: &id008 !ruby/object:Gem::Requirement
119
135
  none: false
120
136
  requirements:
121
137
  - - ">="
@@ -126,12 +142,12 @@ dependencies:
126
142
  - 9
127
143
  - 2
128
144
  version: 0.9.2
129
- requirement: *id007
145
+ requirement: *id008
130
146
  type: :development
131
147
  name: cucumber
132
148
  prerelease: false
133
149
  - !ruby/object:Gem::Dependency
134
- version_requirements: &id008 !ruby/object:Gem::Requirement
150
+ version_requirements: &id009 !ruby/object:Gem::Requirement
135
151
  none: false
136
152
  requirements:
137
153
  - - ">="
@@ -142,12 +158,12 @@ dependencies:
142
158
  - 2
143
159
  - 3
144
160
  version: 0.2.3
145
- requirement: *id008
161
+ requirement: *id009
146
162
  type: :development
147
163
  name: aruba
148
164
  prerelease: false
149
165
  - !ruby/object:Gem::Dependency
150
- version_requirements: &id009 !ruby/object:Gem::Requirement
166
+ version_requirements: &id010 !ruby/object:Gem::Requirement
151
167
  none: false
152
168
  requirements:
153
169
  - - ">="
@@ -158,12 +174,12 @@ dependencies:
158
174
  - 8
159
175
  - 7
160
176
  version: 0.8.7
161
- requirement: *id009
177
+ requirement: *id010
162
178
  type: :development
163
179
  name: rake
164
180
  prerelease: false
165
181
  - !ruby/object:Gem::Dependency
166
- version_requirements: &id010 !ruby/object:Gem::Requirement
182
+ version_requirements: &id011 !ruby/object:Gem::Requirement
167
183
  none: false
168
184
  requirements:
169
185
  - - ">="
@@ -174,12 +190,12 @@ dependencies:
174
190
  - 6
175
191
  - 1
176
192
  version: 0.6.1
177
- requirement: *id010
193
+ requirement: *id011
178
194
  type: :development
179
195
  name: yard
180
196
  prerelease: false
181
197
  - !ruby/object:Gem::Dependency
182
- version_requirements: &id011 !ruby/object:Gem::Requirement
198
+ version_requirements: &id012 !ruby/object:Gem::Requirement
183
199
  none: false
184
200
  requirements:
185
201
  - - ">="
@@ -190,7 +206,7 @@ dependencies:
190
206
  - 6
191
207
  - 5
192
208
  version: 1.6.5
193
- requirement: *id011
209
+ requirement: *id012
194
210
  type: :development
195
211
  name: rdiscount
196
212
  prerelease: false
@@ -266,10 +282,12 @@ files:
266
282
  - lib/win32/autogui.rb
267
283
  - lib/win32/autogui/application.rb
268
284
  - lib/win32/autogui/input.rb
285
+ - lib/win32/autogui/logging.rb
269
286
  - lib/win32/autogui/window.rb
270
287
  - lib/win32/autogui/windows/window.rb
271
288
  - spec/applications/calculator.rb
272
289
  - spec/auto_gui/application_spec.rb
290
+ - spec/auto_gui/logging_spec.rb
273
291
  - spec/basic_gem/basic_gem_spec.rb
274
292
  - spec/spec.opts
275
293
  - spec/spec_helper.rb
@@ -323,6 +341,7 @@ test_files:
323
341
  - features/support/env.rb
324
342
  - spec/applications/calculator.rb
325
343
  - spec/auto_gui/application_spec.rb
344
+ - spec/auto_gui/logging_spec.rb
326
345
  - spec/basic_gem/basic_gem_spec.rb
327
346
  - spec/spec.opts
328
347
  - spec/spec_helper.rb