win32-autogui 0.2.1 → 0.3.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.
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