win 0.1.27 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -5
- data/.gitignore +21 -21
- data/LICENSE +20 -20
- data/README.rdoc +175 -175
- data/Rakefile +58 -58
- data/VERSION +1 -1
- data/features/support/env.rb +4 -4
- data/features/win.feature +9 -9
- data/lib/win/dde.rb +1234 -1234
- data/lib/win/error.rb +1223 -1223
- data/lib/win/extensions.rb +41 -41
- data/lib/win/gui.rb +16 -16
- data/lib/win/gui/dialog.rb +50 -50
- data/lib/win/gui/input.rb +319 -319
- data/lib/win/gui/message.rb +807 -807
- data/lib/win/gui/window.rb +679 -679
- data/lib/win/library.rb +463 -463
- data/spec/spec.opts +2 -2
- data/spec/spec_helper.rb +140 -135
- data/spec/test_apps/locknote/LockNote.exe +0 -0
- data/spec/win/dde_spec.rb +528 -528
- data/spec/win/error_spec.rb +112 -112
- data/spec/win/extensions_spec.rb +73 -73
- data/spec/win/gui/dialog_spec.rb +43 -43
- data/spec/win/gui/input_spec.rb +101 -101
- data/spec/win/gui/message_spec.rb +236 -236
- data/spec/win/gui/window_spec.rb +549 -548
- data/spec/win/library_spec.rb +341 -341
- data/win.gemspec +87 -87
- metadata +34 -17
data/spec/win/gui/input_spec.rb
CHANGED
@@ -1,101 +1,101 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
-
require 'win/gui/input'
|
3
|
-
#require 'win/gui/window'
|
4
|
-
|
5
|
-
module WinWindowTest
|
6
|
-
|
7
|
-
include WinTestApp
|
8
|
-
include Win::
|
9
|
-
|
10
|
-
describe Win::
|
11
|
-
|
12
|
-
describe '#keydb_event' do
|
13
|
-
spec{ use{ keybd_event(vkey = 0, bscan = 0, flags = 0, extra_info = 0) }}
|
14
|
-
|
15
|
-
it 'synthesizes a numeric keystrokes, emulating keyboard driver' do
|
16
|
-
test_app do |app|
|
17
|
-
text = '12 34'
|
18
|
-
text.upcase.each_byte do |b| # upcase needed since user32 keybd_event expects upper case chars
|
19
|
-
keybd_event(b.ord, 0, KEYEVENTF_KEYDOWN, 0)
|
20
|
-
sleep TEST_KEY_DELAY
|
21
|
-
keybd_event(b.ord, 0, KEYEVENTF_KEYUP, 0)
|
22
|
-
sleep TEST_KEY_DELAY
|
23
|
-
end
|
24
|
-
text(app.textarea).should =~ Regexp.new(text)
|
25
|
-
5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end # describe '#keydb_event'
|
29
|
-
|
30
|
-
describe '#mouse_event' do
|
31
|
-
spec { use {mouse_event( flags = MOUSEEVENTF_ABSOLUTE , dx = 0, dy = 0, data=0, extra_info=0 )}}
|
32
|
-
it 'Emulates Mouse clicks'
|
33
|
-
end # describe '#mouse_event'
|
34
|
-
|
35
|
-
describe "#get_cursor_pos" do
|
36
|
-
spec{ use{ success = GetCursorPos(lp_point=FFI::MemoryPointer.new(:long, 2)) }}
|
37
|
-
spec{ use{ x, y = get_cursor_pos() }}
|
38
|
-
|
39
|
-
it "original api returns success code and puts cursor's screen coordinates into supplied buffer" do
|
40
|
-
success = GetCursorPos(lp_point=FFI::MemoryPointer.new(:long, 2))
|
41
|
-
success.should_not == 0
|
42
|
-
x, y = lp_point.read_array_of_long(2)
|
43
|
-
x.should be_an Integer
|
44
|
-
x.should be >= 0
|
45
|
-
y.should be_an Integer
|
46
|
-
y.should be >= 0
|
47
|
-
end
|
48
|
-
|
49
|
-
it "snake_case api returns the cursor's position, in screen coordinates" do
|
50
|
-
x, y = get_cursor_pos()
|
51
|
-
x.should be_an Integer
|
52
|
-
x.should be >= 0
|
53
|
-
y.should be_an Integer
|
54
|
-
y.should be >= 0
|
55
|
-
end
|
56
|
-
end # describe get_cursor_pos
|
57
|
-
|
58
|
-
describe '#set_cursor_pos' do
|
59
|
-
spec { use {success = SetCursorPos(x=0, y=0)}}
|
60
|
-
spec { use {success = set_cursor_pos(x=0, y=0)}}
|
61
|
-
|
62
|
-
it 'sets cursor`s position, in screen coordinates' do
|
63
|
-
SetCursorPos(x=600, y=600).should be_true
|
64
|
-
get_cursor_pos().should == [600,600]
|
65
|
-
set_cursor_pos(x=0, y=0).should be_true
|
66
|
-
get_cursor_pos().should == [0,0]
|
67
|
-
end
|
68
|
-
end # describe '#set_cursor_pos'
|
69
|
-
|
70
|
-
end # Win::
|
71
|
-
|
72
|
-
describe Win::
|
73
|
-
describe '#keystroke' do
|
74
|
-
spec{ use{ keystroke( vkey = 30, vkey = 30) }}
|
75
|
-
|
76
|
-
it 'emulates combinations of keys pressed (Ctrl+Alt+P+M, etc)' do
|
77
|
-
test_app do |app|
|
78
|
-
keystroke(VK_CONTROL, 'A'.ord)
|
79
|
-
keystroke(VK_SPACE)
|
80
|
-
text(app.textarea).should.should == ' '
|
81
|
-
2.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end # describe '#keystroke'
|
85
|
-
|
86
|
-
describe '#type_in' do
|
87
|
-
spec{ use{ type_in(message = '') }}
|
88
|
-
|
89
|
-
it 'types text message into the window holding the focus' do
|
90
|
-
test_app do |app|
|
91
|
-
text = '12 34'
|
92
|
-
type_in(text)
|
93
|
-
text(app.textarea).should =~ Regexp.new(text)
|
94
|
-
5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end # describe '#type_in'
|
98
|
-
|
99
|
-
end # Win::
|
100
|
-
end
|
101
|
-
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
require 'win/gui/input'
|
3
|
+
#require 'win/gui/window'
|
4
|
+
|
5
|
+
module WinWindowTest
|
6
|
+
|
7
|
+
include WinTestApp
|
8
|
+
include Win::Gui::Input
|
9
|
+
|
10
|
+
describe Win::Gui::Input, ' defines a set of API functions related to user input' do
|
11
|
+
|
12
|
+
describe '#keydb_event' do
|
13
|
+
spec{ use{ keybd_event(vkey = 0, bscan = 0, flags = 0, extra_info = 0) }}
|
14
|
+
|
15
|
+
it 'synthesizes a numeric keystrokes, emulating keyboard driver' do
|
16
|
+
test_app do |app|
|
17
|
+
text = '12 34'
|
18
|
+
text.upcase.each_byte do |b| # upcase needed since user32 keybd_event expects upper case chars
|
19
|
+
keybd_event(b.ord, 0, KEYEVENTF_KEYDOWN, 0)
|
20
|
+
sleep TEST_KEY_DELAY
|
21
|
+
keybd_event(b.ord, 0, KEYEVENTF_KEYUP, 0)
|
22
|
+
sleep TEST_KEY_DELAY
|
23
|
+
end
|
24
|
+
text(app.textarea).should =~ Regexp.new(text)
|
25
|
+
5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end # describe '#keydb_event'
|
29
|
+
|
30
|
+
describe '#mouse_event' do
|
31
|
+
spec { use {mouse_event( flags = MOUSEEVENTF_ABSOLUTE , dx = 0, dy = 0, data=0, extra_info=0 )}}
|
32
|
+
it 'Emulates Mouse clicks'
|
33
|
+
end # describe '#mouse_event'
|
34
|
+
|
35
|
+
describe "#get_cursor_pos" do
|
36
|
+
spec{ use{ success = GetCursorPos(lp_point=FFI::MemoryPointer.new(:long, 2)) }}
|
37
|
+
spec{ use{ x, y = get_cursor_pos() }}
|
38
|
+
|
39
|
+
it "original api returns success code and puts cursor's screen coordinates into supplied buffer" do
|
40
|
+
success = GetCursorPos(lp_point=FFI::MemoryPointer.new(:long, 2))
|
41
|
+
success.should_not == 0
|
42
|
+
x, y = lp_point.read_array_of_long(2)
|
43
|
+
x.should be_an Integer
|
44
|
+
x.should be >= 0
|
45
|
+
y.should be_an Integer
|
46
|
+
y.should be >= 0
|
47
|
+
end
|
48
|
+
|
49
|
+
it "snake_case api returns the cursor's position, in screen coordinates" do
|
50
|
+
x, y = get_cursor_pos()
|
51
|
+
x.should be_an Integer
|
52
|
+
x.should be >= 0
|
53
|
+
y.should be_an Integer
|
54
|
+
y.should be >= 0
|
55
|
+
end
|
56
|
+
end # describe get_cursor_pos
|
57
|
+
|
58
|
+
describe '#set_cursor_pos' do
|
59
|
+
spec { use {success = SetCursorPos(x=0, y=0)}}
|
60
|
+
spec { use {success = set_cursor_pos(x=0, y=0)}}
|
61
|
+
|
62
|
+
it 'sets cursor`s position, in screen coordinates' do
|
63
|
+
SetCursorPos(x=600, y=600).should be_true
|
64
|
+
get_cursor_pos().should == [600,600]
|
65
|
+
set_cursor_pos(x=0, y=0).should be_true
|
66
|
+
get_cursor_pos().should == [0,0]
|
67
|
+
end
|
68
|
+
end # describe '#set_cursor_pos'
|
69
|
+
|
70
|
+
end # Win::Gui::Input, ' defines a set of API functions related to user input'
|
71
|
+
|
72
|
+
describe Win::Gui::Input, ' defines convenience/service methods on top of Windows API' do
|
73
|
+
describe '#keystroke' do
|
74
|
+
spec{ use{ keystroke( vkey = 30, vkey = 30) }}
|
75
|
+
|
76
|
+
it 'emulates combinations of keys pressed (Ctrl+Alt+P+M, etc)' do
|
77
|
+
test_app do |app|
|
78
|
+
keystroke(VK_CONTROL, 'A'.ord)
|
79
|
+
keystroke(VK_SPACE)
|
80
|
+
text(app.textarea).should.should == ' '
|
81
|
+
2.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end # describe '#keystroke'
|
85
|
+
|
86
|
+
describe '#type_in' do
|
87
|
+
spec{ use{ type_in(message = '') }}
|
88
|
+
|
89
|
+
it 'types text message into the window holding the focus' do
|
90
|
+
test_app do |app|
|
91
|
+
text = '12 34'
|
92
|
+
type_in(text)
|
93
|
+
text(app.textarea).should =~ Regexp.new(text)
|
94
|
+
5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end # describe '#type_in'
|
98
|
+
|
99
|
+
end # Win::Gui::Input, ' defines convenience/service methods on top of Windows API'
|
100
|
+
end
|
101
|
+
|
@@ -1,236 +1,236 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
-
require 'win/gui/input'
|
3
|
-
require 'win/error'
|
4
|
-
|
5
|
-
module
|
6
|
-
|
7
|
-
include WinTestApp
|
8
|
-
include Win::
|
9
|
-
include Win::
|
10
|
-
include Win::
|
11
|
-
include Win::Error
|
12
|
-
|
13
|
-
def buffer
|
14
|
-
@buffer ||= FFI::MemoryPointer.new :char, 1024
|
15
|
-
end
|
16
|
-
|
17
|
-
def msg
|
18
|
-
@msg ||=Win::
|
19
|
-
end
|
20
|
-
|
21
|
-
def msg_callback
|
22
|
-
lambda {|handle, message, data, result| @handle = handle; @message = message; @data = data; @result = result }
|
23
|
-
end
|
24
|
-
|
25
|
-
def should_have msg, members
|
26
|
-
members.each do |member, value|
|
27
|
-
case member
|
28
|
-
when :l_param
|
29
|
-
msg[member].address.should == value
|
30
|
-
when :time
|
31
|
-
msg[member].should be > value
|
32
|
-
else
|
33
|
-
msg[member].should == value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def clear_thread_queue
|
39
|
-
get_message while peek_message
|
40
|
-
end
|
41
|
-
|
42
|
-
describe Win::
|
43
|
-
before(:all){clear_thread_queue}
|
44
|
-
|
45
|
-
describe '#post_message' do
|
46
|
-
before(:each){clear_thread_queue}
|
47
|
-
after(:all){close_test_app if @launched_test_app}
|
48
|
-
|
49
|
-
spec{ use{ success = PostMessage(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
50
|
-
spec{ use{ success = post_message(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
51
|
-
|
52
|
-
it 'places (posts) a message in the message queue associated with the thread that created the specified window' do
|
53
|
-
app = launch_test_app
|
54
|
-
post_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil).should == true
|
55
|
-
sleep TEST_SLEEP_DELAY
|
56
|
-
window?(app.handle).should == false
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'places (posts) a message into current thread`s queue if first arg is 0' do
|
60
|
-
post_message(0, WM_USER, 33, nil).should == true
|
61
|
-
msg = get_message()
|
62
|
-
should_have msg, hwnd: 0, message: WM_USER, w_param: 33, l_param: 0
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'returns without waiting for the thread to process the message'
|
66
|
-
|
67
|
-
end # describe '#post_message'
|
68
|
-
|
69
|
-
describe '#send_message' do
|
70
|
-
spec{ use{ success = SendMessage(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
71
|
-
spec{ use{ success = send_message(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
72
|
-
|
73
|
-
it 'directly sends the specified message to a window or windows' do
|
74
|
-
app = launch_test_app
|
75
|
-
|
76
|
-
num_chars = send_message app.handle, WM_GETTEXT, buffer.size, buffer
|
77
|
-
buffer.get_bytes(0, num_chars).should == "LockNote - Steganos LockNote"
|
78
|
-
|
79
|
-
num_chars = send_message app.textarea, WM_GETTEXT, buffer.size, buffer
|
80
|
-
buffer.get_bytes(0, num_chars).should =~ /Welcome to Steganos LockNote/
|
81
|
-
|
82
|
-
send_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil)
|
83
|
-
sleep TEST_SLEEP_DELAY # delay to allow window close
|
84
|
-
window?(app.handle).should == false
|
85
|
-
end
|
86
|
-
end # describe '#send_message'
|
87
|
-
|
88
|
-
# :call-seq:
|
89
|
-
# success = send_message_callback(handle, msg, w_param, l_param, data)
|
90
|
-
# {|handle, msg, data, l_result| callback code }
|
91
|
-
|
92
|
-
describe "#send_message_callback" do
|
93
|
-
before(:all){@app=launch_test_app}
|
94
|
-
after(:all){close_test_app if @launched_test_app}
|
95
|
-
|
96
|
-
spec{ use{ success = SendMessageCallback(h_wnd=0, msg=0, w_param=0, l_param=nil, msg_callback, data=0) }}
|
97
|
-
spec{ use{ success = send_message_callback(h_wnd=0, msg=0, w_param=0, l_param=nil, data=0, &msg_callback) }}
|
98
|
-
|
99
|
-
it "sends message to window and returns, specifying callback to be called by system after message is processed" do
|
100
|
-
sent = SendMessageCallback(@app.handle, WM_USER, 0, nil, msg_callback, data=13)
|
101
|
-
sent.should == 1
|
102
|
-
@handle.should == nil
|
103
|
-
@message.should == nil
|
104
|
-
@data.should == nil
|
105
|
-
@result.should == nil
|
106
|
-
|
107
|
-
sleep TEST_SLEEP_DELAY # small delay to allow message delivery
|
108
|
-
peek_message # dispatching sent message (even though there is nothing in queue)
|
109
|
-
|
110
|
-
@handle.should == @app.handle
|
111
|
-
@message.should == WM_USER
|
112
|
-
@data.should == 13
|
113
|
-
@result.should == 0
|
114
|
-
end
|
115
|
-
|
116
|
-
it "snake_case api defaults data to 0, converts block into callback and returns true/false" do
|
117
|
-
sent = send_message_callback(@app.handle, WM_USER, 0, nil){|*args|@data=args[2]}
|
118
|
-
sent.should == true
|
119
|
-
@data.should == nil
|
120
|
-
|
121
|
-
sleep TEST_SLEEP_DELAY # small delay to allow message delivery
|
122
|
-
peek_message # dispatching sent message (even though there is nothing in queue)
|
123
|
-
|
124
|
-
@data.should == 0
|
125
|
-
end
|
126
|
-
|
127
|
-
it "fails if unable to send message" do
|
128
|
-
sent = SendMessageCallback(not_a_handle, WM_USER, 0, nil, msg_callback, 0)
|
129
|
-
sent.should == 0
|
130
|
-
send_message_callback(not_a_handle, WM_USER, 0, nil){|*args|@data=args[2]}
|
131
|
-
sent.should == 0
|
132
|
-
get_last_error.should == "Invalid window handle."
|
133
|
-
end
|
134
|
-
end # describe send_message_callback
|
135
|
-
|
136
|
-
describe "#get_message" do
|
137
|
-
before(:all){clear_thread_queue; 2.times {post_message 0,0,0,nil}}
|
138
|
-
|
139
|
-
spec{ use{ res = GetMessage(msg, handle=0, msg_filter_min=0, msg_filter_max=0) }}
|
140
|
-
spec{ use{ message = get_message(msg, handle=0, msg_filter_min=0, msg_filter_max=0) }}
|
141
|
-
|
142
|
-
it "original api retrieves a message from the calling thread's message queue" do
|
143
|
-
set_cursor_pos(x=0, y=0)
|
144
|
-
post_message(0, WM_USER+1, 33, nil)
|
145
|
-
res = GetMessage(msg, handle=0, msg_filter_min=0, msg_filter_max=0)
|
146
|
-
res.should == 1
|
147
|
-
# p msg[:hwnd], msg[:message], msg[:w_param], msg[:l_param], msg[:time], msg[:x], msg[:y]
|
148
|
-
should_have msg, hwnd: 0, message: WM_USER+1, w_param: 33, x: 0, y: 0, l_param: 0, time: 1000000
|
149
|
-
end
|
150
|
-
|
151
|
-
it "original api returns -1 if there is an error (wrong handle, in this case)" do
|
152
|
-
res = GetMessage(msg, not_a_handle, msg_filter_min=0, msg_filter_max=0)
|
153
|
-
res.should == -1
|
154
|
-
end
|
155
|
-
|
156
|
-
it "original api returns 0 if WM_QUIT was posted to thread`s message queue" do
|
157
|
-
post_message(0, WM_QUIT, 13, nil)
|
158
|
-
res = GetMessage(msg, 0, msg_filter_min=0, msg_filter_max=0)
|
159
|
-
res.should == 0
|
160
|
-
end
|
161
|
-
|
162
|
-
it "snake_case api returns a message struct retrieved from the calling thread's message queue " do
|
163
|
-
set_cursor_pos(x=99, y=99)
|
164
|
-
post_message(0, WM_USER+2, 33, nil)
|
165
|
-
msg = get_message()
|
166
|
-
should_have msg, hwnd: 0, message: WM_USER+2, w_param: 33, x: 99, y: 99, l_param: 0, time: 1000000
|
167
|
-
end
|
168
|
-
|
169
|
-
it "snake_case api returns nil if there is an error (wrong handle, in this case)" do
|
170
|
-
get_message(msg, not_a_handle).should == nil
|
171
|
-
end
|
172
|
-
|
173
|
-
it "snake_case api returns false if WM_QUIT was posted to thread`s message queue" do
|
174
|
-
post_message(0, WM_QUIT, 13, nil)
|
175
|
-
get_message.should == false
|
176
|
-
end
|
177
|
-
end # describe get_message
|
178
|
-
|
179
|
-
|
180
|
-
describe "#peek_message" do
|
181
|
-
before(:all){set_cursor_pos(x=0, y=0); post_message(0, WM_USER+2, 13, nil)}
|
182
|
-
spec{ use{ success = PeekMessage(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0) }}
|
183
|
-
spec{ use{ success = peek_message(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0) }}
|
184
|
-
|
185
|
-
it "original api checks the thread message queue for a posted message, retrieves it without removing" do
|
186
|
-
10.times do
|
187
|
-
res = PeekMessage(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0)
|
188
|
-
res.should == 1
|
189
|
-
should_have msg, hwnd: 0, message: WM_USER+2, w_param: 13, x: 0, y: 0, l_param: 0, time: 1000000
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
it "snake_case api checks the thread message queue for a posted message, returns it without removing" do
|
194
|
-
10.times do
|
195
|
-
msg = peek_message()
|
196
|
-
should_have msg, hwnd: 0, message: WM_USER+2, w_param: 13, x: 0, y: 0, l_param: 0, time: 1000000
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
it "original api returns 0 if no message in queue" do
|
201
|
-
get_message
|
202
|
-
PeekMessage(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0).should == 0
|
203
|
-
end
|
204
|
-
|
205
|
-
it "snake_case api returns nil if no message in queue" do
|
206
|
-
peek_message.should == nil
|
207
|
-
end
|
208
|
-
end # describe peek_message
|
209
|
-
|
210
|
-
describe "#translate_message" do
|
211
|
-
spec{ use{ success = TranslateMessage(msg) }}
|
212
|
-
spec{ use{ success = translate_message(msg) }}
|
213
|
-
|
214
|
-
it "translates virtual-key message into character message which is then posted to the thread's message queue"
|
215
|
-
|
216
|
-
it "returns zero/false if no translation took place" do
|
217
|
-
TranslateMessage(msg).should == 0
|
218
|
-
translate_message(msg).should == false
|
219
|
-
end
|
220
|
-
end # describe translate_message
|
221
|
-
|
222
|
-
describe "#dispatch_message" do
|
223
|
-
spec{ use{ res = DispatchMessage(msg) }} #return value is normally ignored
|
224
|
-
spec{ use{ res = dispatch_message(msg) }} #return value is normally ignored
|
225
|
-
|
226
|
-
it "dispatches a message to a window procedure. Typically used to dispatch a message retrieved by GetMessage" do
|
227
|
-
pending
|
228
|
-
res = DispatchMessage(msg)
|
229
|
-
end
|
230
|
-
|
231
|
-
end # describe dispatch_message
|
232
|
-
|
233
|
-
end # Win::
|
234
|
-
end
|
235
|
-
|
236
|
-
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
require 'win/gui/input'
|
3
|
+
require 'win/error'
|
4
|
+
|
5
|
+
module WinGuiMessageTest
|
6
|
+
|
7
|
+
include WinTestApp
|
8
|
+
include Win::Gui::Message
|
9
|
+
include Win::Gui::Window
|
10
|
+
include Win::Gui::Input
|
11
|
+
include Win::Error
|
12
|
+
|
13
|
+
def buffer
|
14
|
+
@buffer ||= FFI::MemoryPointer.new :char, 1024
|
15
|
+
end
|
16
|
+
|
17
|
+
def msg
|
18
|
+
@msg ||=Win::Gui::Message::Msg.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def msg_callback
|
22
|
+
lambda {|handle, message, data, result| @handle = handle; @message = message; @data = data; @result = result }
|
23
|
+
end
|
24
|
+
|
25
|
+
def should_have msg, members
|
26
|
+
members.each do |member, value|
|
27
|
+
case member
|
28
|
+
when :l_param
|
29
|
+
msg[member].address.should == value
|
30
|
+
when :time
|
31
|
+
msg[member].should be > value
|
32
|
+
else
|
33
|
+
msg[member].should == value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear_thread_queue
|
39
|
+
get_message while peek_message
|
40
|
+
end
|
41
|
+
|
42
|
+
describe Win::Gui::Message, ' defines a set of API functions related to Window messaging' do
|
43
|
+
before(:all){clear_thread_queue}
|
44
|
+
|
45
|
+
describe '#post_message' do
|
46
|
+
before(:each){clear_thread_queue}
|
47
|
+
after(:all){close_test_app if @launched_test_app}
|
48
|
+
|
49
|
+
spec{ use{ success = PostMessage(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
50
|
+
spec{ use{ success = post_message(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
51
|
+
|
52
|
+
it 'places (posts) a message in the message queue associated with the thread that created the specified window' do
|
53
|
+
app = launch_test_app
|
54
|
+
post_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil).should == true
|
55
|
+
sleep TEST_SLEEP_DELAY
|
56
|
+
window?(app.handle).should == false
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'places (posts) a message into current thread`s queue if first arg is 0' do
|
60
|
+
post_message(0, WM_USER, 33, nil).should == true
|
61
|
+
msg = get_message()
|
62
|
+
should_have msg, hwnd: 0, message: WM_USER, w_param: 33, l_param: 0
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns without waiting for the thread to process the message'
|
66
|
+
|
67
|
+
end # describe '#post_message'
|
68
|
+
|
69
|
+
describe '#send_message' do
|
70
|
+
spec{ use{ success = SendMessage(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
71
|
+
spec{ use{ success = send_message(handle = 0, msg = 0, w_param = 0, l_param = nil) }}
|
72
|
+
|
73
|
+
it 'directly sends the specified message to a window or windows' do
|
74
|
+
app = launch_test_app
|
75
|
+
|
76
|
+
num_chars = send_message app.handle, WM_GETTEXT, buffer.size, buffer
|
77
|
+
buffer.get_bytes(0, num_chars).should == "LockNote - Steganos LockNote"
|
78
|
+
|
79
|
+
num_chars = send_message app.textarea, WM_GETTEXT, buffer.size, buffer
|
80
|
+
buffer.get_bytes(0, num_chars).should =~ /Welcome to Steganos LockNote/
|
81
|
+
|
82
|
+
send_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil)
|
83
|
+
sleep TEST_SLEEP_DELAY # delay to allow window close
|
84
|
+
window?(app.handle).should == false
|
85
|
+
end
|
86
|
+
end # describe '#send_message'
|
87
|
+
|
88
|
+
# :call-seq:
|
89
|
+
# success = send_message_callback(handle, msg, w_param, l_param, data)
|
90
|
+
# {|handle, msg, data, l_result| callback code }
|
91
|
+
|
92
|
+
describe "#send_message_callback" do
|
93
|
+
before(:all){@app=launch_test_app}
|
94
|
+
after(:all){close_test_app if @launched_test_app}
|
95
|
+
|
96
|
+
spec{ use{ success = SendMessageCallback(h_wnd=0, msg=0, w_param=0, l_param=nil, msg_callback, data=0) }}
|
97
|
+
spec{ use{ success = send_message_callback(h_wnd=0, msg=0, w_param=0, l_param=nil, data=0, &msg_callback) }}
|
98
|
+
|
99
|
+
it "sends message to window and returns, specifying callback to be called by system after message is processed" do
|
100
|
+
sent = SendMessageCallback(@app.handle, WM_USER, 0, nil, msg_callback, data=13)
|
101
|
+
sent.should == 1
|
102
|
+
@handle.should == nil
|
103
|
+
@message.should == nil
|
104
|
+
@data.should == nil
|
105
|
+
@result.should == nil
|
106
|
+
|
107
|
+
sleep TEST_SLEEP_DELAY # small delay to allow message delivery
|
108
|
+
peek_message # dispatching sent message (even though there is nothing in queue)
|
109
|
+
|
110
|
+
@handle.should == @app.handle
|
111
|
+
@message.should == WM_USER
|
112
|
+
@data.should == 13
|
113
|
+
@result.should == 0
|
114
|
+
end
|
115
|
+
|
116
|
+
it "snake_case api defaults data to 0, converts block into callback and returns true/false" do
|
117
|
+
sent = send_message_callback(@app.handle, WM_USER, 0, nil){|*args|@data=args[2]}
|
118
|
+
sent.should == true
|
119
|
+
@data.should == nil
|
120
|
+
|
121
|
+
sleep TEST_SLEEP_DELAY # small delay to allow message delivery
|
122
|
+
peek_message # dispatching sent message (even though there is nothing in queue)
|
123
|
+
|
124
|
+
@data.should == 0
|
125
|
+
end
|
126
|
+
|
127
|
+
it "fails if unable to send message" do
|
128
|
+
sent = SendMessageCallback(not_a_handle, WM_USER, 0, nil, msg_callback, 0)
|
129
|
+
sent.should == 0
|
130
|
+
send_message_callback(not_a_handle, WM_USER, 0, nil){|*args|@data=args[2]}
|
131
|
+
sent.should == 0
|
132
|
+
get_last_error.should == "Invalid window handle."
|
133
|
+
end
|
134
|
+
end # describe send_message_callback
|
135
|
+
|
136
|
+
describe "#get_message" do
|
137
|
+
before(:all){clear_thread_queue; 2.times {post_message 0,0,0,nil}}
|
138
|
+
|
139
|
+
spec{ use{ res = GetMessage(msg, handle=0, msg_filter_min=0, msg_filter_max=0) }}
|
140
|
+
spec{ use{ message = get_message(msg, handle=0, msg_filter_min=0, msg_filter_max=0) }}
|
141
|
+
|
142
|
+
it "original api retrieves a message from the calling thread's message queue" do
|
143
|
+
set_cursor_pos(x=0, y=0)
|
144
|
+
post_message(0, WM_USER+1, 33, nil)
|
145
|
+
res = GetMessage(msg, handle=0, msg_filter_min=0, msg_filter_max=0)
|
146
|
+
res.should == 1
|
147
|
+
# p msg[:hwnd], msg[:message], msg[:w_param], msg[:l_param], msg[:time], msg[:x], msg[:y]
|
148
|
+
should_have msg, hwnd: 0, message: WM_USER+1, w_param: 33, x: 0, y: 0, l_param: 0, time: 1000000
|
149
|
+
end
|
150
|
+
|
151
|
+
it "original api returns -1 if there is an error (wrong handle, in this case)" do
|
152
|
+
res = GetMessage(msg, not_a_handle, msg_filter_min=0, msg_filter_max=0)
|
153
|
+
res.should == -1
|
154
|
+
end
|
155
|
+
|
156
|
+
it "original api returns 0 if WM_QUIT was posted to thread`s message queue" do
|
157
|
+
post_message(0, WM_QUIT, 13, nil)
|
158
|
+
res = GetMessage(msg, 0, msg_filter_min=0, msg_filter_max=0)
|
159
|
+
res.should == 0
|
160
|
+
end
|
161
|
+
|
162
|
+
it "snake_case api returns a message struct retrieved from the calling thread's message queue " do
|
163
|
+
set_cursor_pos(x=99, y=99)
|
164
|
+
post_message(0, WM_USER+2, 33, nil)
|
165
|
+
msg = get_message()
|
166
|
+
should_have msg, hwnd: 0, message: WM_USER+2, w_param: 33, x: 99, y: 99, l_param: 0, time: 1000000
|
167
|
+
end
|
168
|
+
|
169
|
+
it "snake_case api returns nil if there is an error (wrong handle, in this case)" do
|
170
|
+
get_message(msg, not_a_handle).should == nil
|
171
|
+
end
|
172
|
+
|
173
|
+
it "snake_case api returns false if WM_QUIT was posted to thread`s message queue" do
|
174
|
+
post_message(0, WM_QUIT, 13, nil)
|
175
|
+
get_message.should == false
|
176
|
+
end
|
177
|
+
end # describe get_message
|
178
|
+
|
179
|
+
|
180
|
+
describe "#peek_message" do
|
181
|
+
before(:all){set_cursor_pos(x=0, y=0); post_message(0, WM_USER+2, 13, nil)}
|
182
|
+
spec{ use{ success = PeekMessage(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0) }}
|
183
|
+
spec{ use{ success = peek_message(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0) }}
|
184
|
+
|
185
|
+
it "original api checks the thread message queue for a posted message, retrieves it without removing" do
|
186
|
+
10.times do
|
187
|
+
res = PeekMessage(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0)
|
188
|
+
res.should == 1
|
189
|
+
should_have msg, hwnd: 0, message: WM_USER+2, w_param: 13, x: 0, y: 0, l_param: 0, time: 1000000
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "snake_case api checks the thread message queue for a posted message, returns it without removing" do
|
194
|
+
10.times do
|
195
|
+
msg = peek_message()
|
196
|
+
should_have msg, hwnd: 0, message: WM_USER+2, w_param: 13, x: 0, y: 0, l_param: 0, time: 1000000
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "original api returns 0 if no message in queue" do
|
201
|
+
get_message
|
202
|
+
PeekMessage(msg, h_wnd=0, filter_min=0, filter_max=0, remove_msg=0).should == 0
|
203
|
+
end
|
204
|
+
|
205
|
+
it "snake_case api returns nil if no message in queue" do
|
206
|
+
peek_message.should == nil
|
207
|
+
end
|
208
|
+
end # describe peek_message
|
209
|
+
|
210
|
+
describe "#translate_message" do
|
211
|
+
spec{ use{ success = TranslateMessage(msg) }}
|
212
|
+
spec{ use{ success = translate_message(msg) }}
|
213
|
+
|
214
|
+
it "translates virtual-key message into character message which is then posted to the thread's message queue"
|
215
|
+
|
216
|
+
it "returns zero/false if no translation took place" do
|
217
|
+
TranslateMessage(msg).should == 0
|
218
|
+
translate_message(msg).should == false
|
219
|
+
end
|
220
|
+
end # describe translate_message
|
221
|
+
|
222
|
+
describe "#dispatch_message" do
|
223
|
+
spec{ use{ res = DispatchMessage(msg) }} #return value is normally ignored
|
224
|
+
spec{ use{ res = dispatch_message(msg) }} #return value is normally ignored
|
225
|
+
|
226
|
+
it "dispatches a message to a window procedure. Typically used to dispatch a message retrieved by GetMessage" do
|
227
|
+
pending
|
228
|
+
res = DispatchMessage(msg)
|
229
|
+
end
|
230
|
+
|
231
|
+
end # describe dispatch_message
|
232
|
+
|
233
|
+
end # Win::Gui::Message, ' defines a set of API functions related to Window messaging'
|
234
|
+
end
|
235
|
+
|
236
|
+
|