win 0.1.27 → 0.3.1

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/spec/spec.opts CHANGED
@@ -1,2 +1,2 @@
1
- --color
2
- --format nested
1
+ --color
2
+ --format nested
data/spec/spec_helper.rb CHANGED
@@ -1,136 +1,141 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'spec'
4
- require 'spec/autorun'
5
- require 'win/gui'
6
-
7
- $debug = true
8
-
9
- # Customize RSpec with my own extensions
10
- module ClassMacros
11
-
12
- def os
13
- `ver`
14
- end
15
-
16
- def vista?
17
- os =~ /Version 6/
18
- end
19
-
20
- def xp?
21
- os =~ /XP/
22
- end
23
-
24
- # wrapper for it method that extracts description from example source code, such as:
25
- # spec { use{ function(arg1 = 4, arg2 = 'string') }}
26
- def spec &block
27
- it description_from(caller[0]), &block # it description_from(*block.source_location), &block
28
- #do lambda(&block).should_not raise_error end
29
- end
30
-
31
- # reads description line from source file and drops external brackets like its{}, use{}
32
- # accepts as arguments either file name and line or call stack member (caller[0])
33
- def description_from(*args)
34
- case args.size
35
- when 1
36
- file, line = args.first.scan(/\A(.*?):(\d+)/).first
37
- when 2
38
- file, line = args
39
- end
40
- File.open(file) do |f|
41
- f.lines.to_a[line.to_i-1].gsub( /(spec.*?{)|(use.*?{)|}/, '' ).strip
42
- end
43
- end
44
- end
45
-
46
- # Customize RSpec with my own extensions
47
- module InstanceMacros
48
- def use
49
- lambda{yield}.should_not raise_error
50
- end
51
-
52
- def any_block
53
- lambda{|*args| args}
54
- end
55
- end
56
-
57
- Spec::Runner.configure do |config|
58
- config.extend(ClassMacros)
59
- config.include(InstanceMacros)
60
-
61
- class << Spec::ExampleGroup
62
- # def spoc &block
63
- # it description_from(caller[0]), &block
64
- # end
65
- end
66
- end
67
-
68
- module WinTest
69
-
70
- TEST_KEY_DELAY = 0.001
71
- TEST_IMPOSSIBLE = 'Impossible'
72
- TEST_CONVERSION_ERROR = /Can.t convert/
73
- TEST_SLEEP_DELAY = 0.02
74
- TEST_APP_PATH = File.join(File.dirname(__FILE__), "test_apps/locknote/LockNote.exe" )
75
- TEST_APP_START = 'start "" "' + TEST_APP_PATH + '"'
76
- TEST_WIN_TITLE = 'LockNote - Steganos LockNote'
77
- TEST_WIN_CLASS = 'ATL:00434098'
78
- TEST_WIN_RECT = [710, 400, 1210, 800]
79
- TEST_STATUSBAR_CLASS = 'msctls_statusbar32'
80
- TEST_TEXTAREA_CLASS = 'ATL:00434310'
81
-
82
- def any_handle
83
- find_window(nil, nil)
84
- end
85
-
86
- def not_a_handle
87
- 123
88
- end
89
-
90
- def buffer
91
- FFI::MemoryPointer.new(:char, 1024)
92
- end
93
- end
94
-
95
- module WinTestApp
96
-
97
- include WinTest
98
- include Win::GUI
99
- #include Win::GUI::Convenience
100
-
101
- def launch_test_app
102
- system TEST_APP_START
103
- sleep TEST_SLEEP_DELAY until (handle = find_window(nil, TEST_WIN_TITLE))
104
-
105
- textarea = find_window_ex(handle, 0, TEST_TEXTAREA_CLASS, nil)
106
- app = "Locknote" # App identifier
107
-
108
- eigenklass = class << app; self; end # Extracting app's eigenclass
109
- eigenklass.class_eval do # Defining singleton methods on app
110
- define_method(:handle) {handle}
111
- define_method(:textarea) {textarea}
112
- end
113
-
114
- @launched_test_app = app
115
- end
116
-
117
- def close_test_app(app = @launched_test_app)
118
- while app && app.respond_to?( :handle) && find_window(nil, TEST_WIN_TITLE)
119
- shut_window app.handle
120
- sleep TEST_SLEEP_DELAY
121
- end
122
- @launched_test_app = nil
123
- end
124
-
125
- # Creates test app object and yields it back to the block
126
- def test_app
127
- app = launch_test_app
128
-
129
- # def app.textarea #define singleton method retrieving app's text area
130
- # Window::Window.new find_window_ex(self.handle, 0, TEST_TEXTAREA_CLASS, nil)
131
- # end
132
-
133
- yield app
134
- close_test_app app
135
- end
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+ require 'win/gui'
6
+
7
+ $debug = true
8
+
9
+ # Customize RSpec with my own extensions
10
+ module ClassMacros
11
+
12
+ # wrapper for it method that extracts description from example source code, such as:
13
+ # spec { use{ function(arg1 = 4, arg2 = 'string') }}
14
+ def spec &block
15
+ it description_from(caller[0]), &block # it description_from(*block.source_location), &block
16
+ #do lambda(&block).should_not raise_error end
17
+ end
18
+
19
+ # reads description line from source file and drops external brackets like its{}, use{}
20
+ # accepts as arguments either file name and line or call stack member (caller[0])
21
+ def description_from(*args)
22
+ case args.size
23
+ when 1
24
+ file, line = args.first.scan(/\A(.*?):(\d+)/).first
25
+ when 2
26
+ file, line = args
27
+ end
28
+ File.open(file) do |f|
29
+ f.lines.to_a[line.to_i-1].gsub( /(spec.*?{)|(use.*?{)|}/, '' ).strip
30
+ end
31
+ end
32
+ end
33
+
34
+ # Customize RSpec with my own extensions
35
+ module InstanceMacros
36
+ def use
37
+ lambda{yield}.should_not raise_error
38
+ end
39
+
40
+ def any_block
41
+ lambda{|*args| args}
42
+ end
43
+ end
44
+
45
+ Spec::Runner.configure do |config|
46
+ config.extend(ClassMacros)
47
+ config.include(InstanceMacros)
48
+
49
+ class << Spec::ExampleGroup
50
+ # def spoc &block
51
+ # it description_from(caller[0]), &block
52
+ # end
53
+ end
54
+ end
55
+
56
+ # Global test methods
57
+ def cygwin?
58
+ @cygwin_flag ||= `ruby -v` =~ /cygwin/
59
+ end
60
+
61
+ def os
62
+ @os_flag ||= cygwin? ? `cmd /c ver` : `ver`
63
+ end
64
+
65
+ def vista?
66
+ os =~ /Version 6/
67
+ end
68
+
69
+ def xp?
70
+ os =~ /XP/
71
+ end
72
+
73
+ module WinTest
74
+
75
+ TEST_KEY_DELAY = 0.001
76
+ TEST_IMPOSSIBLE = 'Impossible'
77
+ TEST_CONVERSION_ERROR = /Can.t convert/
78
+ TEST_SLEEP_DELAY = 0.02
79
+ TEST_APP_PATH = File.join(File.dirname(__FILE__), "test_apps/locknote/LockNote.exe" )
80
+ TEST_APP_START = cygwin? ? "cmd /c start `cygpath -w #{TEST_APP_PATH}`" : 'start "" "' + TEST_APP_PATH + '"'
81
+ TEST_WIN_TITLE = 'LockNote - Steganos LockNote'
82
+ TEST_WIN_CLASS = 'ATL:00434098'
83
+ TEST_WIN_RECT = [710, 400, 1210, 800]
84
+ TEST_STATUSBAR_CLASS = 'msctls_statusbar32'
85
+ TEST_TEXTAREA_CLASS = 'ATL:00434310'
86
+
87
+ def any_handle
88
+ find_window(nil, nil)
89
+ end
90
+
91
+ def not_a_handle
92
+ 123
93
+ end
94
+
95
+ def buffer
96
+ FFI::MemoryPointer.new(:char, 1024)
97
+ end
98
+ end
99
+
100
+ module WinTestApp
101
+
102
+ include WinTest
103
+ include Win::Gui
104
+ #include Win::Gui::Convenience
105
+
106
+ def launch_test_app
107
+ system TEST_APP_START
108
+ sleep TEST_SLEEP_DELAY until (handle = find_window(nil, TEST_WIN_TITLE))
109
+
110
+ textarea = find_window_ex(handle, 0, TEST_TEXTAREA_CLASS, nil)
111
+ app = "Locknote" # App identifier
112
+
113
+ eigenklass = class << app; self; end # Extracting app's eigenclass
114
+ eigenklass.class_eval do # Defining singleton methods on app
115
+ define_method(:handle) {handle}
116
+ define_method(:textarea) {textarea}
117
+ end
118
+
119
+ @launched_test_app = app
120
+ end
121
+
122
+ def close_test_app(app = @launched_test_app)
123
+ while app && app.respond_to?( :handle) && find_window(nil, TEST_WIN_TITLE)
124
+ shut_window app.handle
125
+ sleep TEST_SLEEP_DELAY
126
+ end
127
+ @launched_test_app = nil
128
+ end
129
+
130
+ # Creates test app object and yields it back to the block
131
+ def test_app
132
+ app = launch_test_app
133
+
134
+ # def app.textarea #define singleton method retrieving app's text area
135
+ # Window::Window.new find_window_ex(self.handle, 0, TEST_TEXTAREA_CLASS, nil)
136
+ # end
137
+
138
+ yield app
139
+ close_test_app app
140
+ end
136
141
  end
File without changes
data/spec/win/dde_spec.rb CHANGED
@@ -1,529 +1,529 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- require 'win/dde'
3
-
4
- module WinDDETest
5
- include WinTest
6
- include Win::DDE
7
- include Win::GUI::Message
8
-
9
- POKE_STRING = "Poke_string"
10
-
11
- def dde_cmd
12
- APPCLASS_STANDARD
13
- end
14
-
15
- def dde_callback
16
- ->(*args){}
17
- end
18
-
19
- def zero_id
20
- FFI::MemoryPointer.new(:long).write_long(0)
21
- end
22
-
23
- def buffer
24
- FFI::MemoryPointer.new(:char, 1024)
25
- end
26
-
27
- def string_pointer
28
- FFI::MemoryPointer.from_string("Pointer_string")
29
- end
30
-
31
- def extract_values(*args)
32
- type, format, conv, hsz1, hsz2, data, data1, data2 = *args
33
- @server_conv = conv
34
- [Win::DDE::TYPES[type], format, conv,
35
- dde_query_string(@client_id, hsz1),
36
- dde_query_string(@client_id, hsz2),
37
- data, data1, data2]
38
- end
39
-
40
- def setup_server(&server_block)
41
- @client_calls = []
42
- @server_calls = []
43
- @client_id, st = dde_initialize(APPCLASS_STANDARD) {|*args| @client_calls << extract_values(*args); DDE_FACK}
44
- @server_id, st = dde_initialize(APPCLASS_STANDARD,
45
- &server_block || proc {|*args| @server_calls << extract_values(*args); DDE_FACK} )
46
- @service_handle = dde_create_string_handle(@server_id, 'service 2', CP_WINANSI)
47
- @topic_handle = dde_create_string_handle(@server_id, 'topic 2', CP_WINANSI)
48
- dde_name_service(@server_id, @service_handle, DNS_REGISTER)
49
- end
50
-
51
- def teardown_server
52
- if @print
53
- p @server_calls, @client_calls
54
- p @server_conv
55
- p ERRORS[dde_get_last_error(@server_id)]
56
- p ERRORS[dde_get_last_error(@client_id)]
57
- @print = nil
58
- end
59
- dde_name_service(@server_id, @service_handle, DNS_UNREGISTER) if @server_id && @service_handle
60
- dde_free_string_handle(@server_id, @service_handle) if @server_id && @service_handle
61
- dde_free_string_handle(@server_id, @topic_handle) if @server_id && @topic_handle
62
- dde_uninitialize(@server_id) if @server_id
63
- dde_uninitialize(@client_id) if @client_id
64
- if @conv_handle
65
- dde_disconnect(@conv_handle)
66
- @conv_handle = nil
67
- end
68
- @data = nil
69
- end
70
-
71
- describe Win::DDE, ' contains a set of pre-defined Windows API functions' do
72
-
73
- describe '#register_clipboard_format' do
74
- spec{ use{ RegisterClipboardFormat(format_name = "XlTable") }}
75
- spec{ use{ register_clipboard_format(format_name = "XlTable") }}
76
-
77
- it 'returns format id (int) if successfully registered format' do
78
- id = register_clipboard_format("XlTable")
79
- id.should_not == 0
80
- id.should_not == nil
81
- end
82
-
83
- it 'returns same format id for already registered format' do
84
- id1 = register_clipboard_format("XlTable")
85
- id2 = register_clipboard_format("XlTable")
86
- id1.should == id2
87
- end
88
-
89
- it 'returns nil if not able to register format' do
90
- register_clipboard_format("").should == nil
91
- end
92
- end
93
-
94
- describe '#dde_initialize' do
95
- after(:each) {dde_uninitialize(@id) if @id}
96
-
97
- spec{ use{ status = DdeInitialize( id = zero_id, dde_callback, dde_cmd, unused=0); @id = id.read_long}}
98
- spec{ use{ @id, status = dde_initialize(@id=0, dde_cmd, &dde_callback) }}
99
-
100
- it 'with zero instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
101
- @id, status = dde_initialize(0, APPCLASS_STANDARD) {|*args| }
102
- @id.should be_an Integer
103
- @id.should_not == 0
104
- status.should == DMLERR_NO_ERROR
105
- end
106
-
107
- it 'with nil instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
108
- @id, status = dde_initialize(nil, APPCLASS_STANDARD) {|*args| }
109
- @id.should be_an Integer
110
- @id.should_not == 0
111
- status.should == DMLERR_NO_ERROR
112
- end
113
-
114
- it 'with omitted instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
115
- @id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
116
- @id.should be_an Integer
117
- @id.should_not == 0
118
- status.should == DMLERR_NO_ERROR
119
- end
120
-
121
- it 'returns error status if initialization unsuccessful' do
122
- @id, status = dde_initialize(12345, APPCLASS_STANDARD) {|*args| }
123
- status.should == DMLERR_INVALIDPARAMETER
124
- @id.should == nil
125
- end
126
-
127
- it 'is able to reinitialize with correct id' do
128
- @id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
129
- new_id, status = dde_initialize(@id, APPCLASS_STANDARD) {|*args| }
130
- status.should == DMLERR_NO_ERROR
131
- new_id.should == @id
132
- end
133
- end # describe 'dde_initialize'
134
-
135
- context 'after initialization:' do
136
- before(:each) {@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }}
137
- after(:each) {dde_uninitialize(@id) if @id}
138
-
139
- describe '#dde_uninitialize' do
140
-
141
- spec{ use{ status = DdeUninitialize( @id ) }}
142
- spec{ use{ id, status = dde_uninitialize( @id) }}
143
-
144
- it 'returns true if uninitialization successful' do
145
- res = dde_uninitialize(@id)
146
- res.should == true
147
- end
148
-
149
- it 'returns false if initialization unsuccessful' do
150
- res = dde_uninitialize(12345)
151
- res.should == false
152
- end
153
- end # describe '#dde_uninitialize'
154
-
155
- describe '#dde_create_string_handle' do
156
- after(:each) {dde_free_string_handle(@id, @string_handle) if @string_handle}
157
-
158
- spec{ use{ @string_handle = DdeCreateStringHandle(id=0, string_pointer, code_page_id=CP_WINANSI) }}
159
- spec{ use{ @string_handle = dde_create_string_handle(id=0, string='Any String', code_page_id=CP_WINANSI)}}
160
-
161
- it 'returns nonzero Integer handle to a string (passable to other DDEML functions)' do
162
- @string_handle = dde_create_string_handle(@id, 'My String', CP_WINANSI)
163
- @string_handle.should be_an Integer
164
- @string_handle.should_not == 0
165
- end
166
-
167
- it 'creates handle even if code_page is omitted' do
168
- @string_handle = dde_create_string_handle(@id, 'My String')
169
- @string_handle.should be_an Integer
170
- @string_handle.should_not == 0
171
- end
172
-
173
- it 'creating two handles for the SAME string (inside one instance) USUALLY returns same handle' do
174
- @string_handle = dde_create_string_handle(@id, 'My String')
175
- 10.times do
176
- string_handle1 = dde_create_string_handle(@id, 'My String')
177
- string_handle1.should == @string_handle
178
- dde_free_string_handle(@id, string_handle1)
179
- end
180
- end
181
-
182
- it 'created different handles for two different strings ' do
183
- @string_handle = dde_create_string_handle(@id, 'My String')
184
- string_handle1 = dde_create_string_handle(@id, 'My String1')
185
- string_handle1.should_not == @string_handle
186
- dde_free_string_handle(@id, string_handle1)
187
- end
188
-
189
- it 'returns nil if unable to register handle to a string' do
190
- @string_handle = dde_create_string_handle(@id, "", CP_WINANSI)
191
- @string_handle.should == nil
192
- end
193
- end # describe '#dde_create_string_handle'
194
-
195
- context "with dde string handle to 'My String 2'" do
196
- before(:each) {@string_handle = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)}
197
- after(:each) {dde_free_string_handle(@id, @string_handle)}
198
-
199
- describe '#dde_query_string' do
200
-
201
- spec{ use{ string = DdeQueryString(@id, @string_handle, buffer, buffer.size, code_page=CP_WINANSI)}}
202
- spec{ use{ string = dde_query_string(@id, @string_handle, code_page=CP_WINANSI )}}
203
-
204
- it 'retrieves string that given string handle refers to' do
205
- num_chars = DdeQueryString(@id, @string_handle, buf = buffer, buf.size, CP_WINANSI)
206
- num_chars.should == 11
207
- buf.read_string.should == 'My String 2'
208
- end
209
-
210
- it 'retrieves string that given string handle refers to' do
211
- string = dde_query_string(@id, @string_handle, CP_WINANSI)
212
- string.should == 'My String 2'
213
- end
214
-
215
- it 'retrieves string even if code_page is omitted' do
216
- string = dde_query_string(@id, @string_handle)
217
- string.should == 'My String 2'
218
- end
219
-
220
- it 'returns nil attempting to retrieve invalid handle' do
221
- string = dde_query_string(@id, 12345)
222
- string.should == nil
223
- end
224
- end # describe '#dde_query_string'
225
-
226
- describe '#dde_free_string_handle' do
227
-
228
- spec{ use{ success = DdeFreeStringHandle( @id, @string_handle)}}
229
- spec{ use{ success = dde_free_string_handle( @id, @string_handle )}}
230
-
231
- it 'returns true when freeing string handle registered with DDEML' do
232
- res = dde_free_string_handle(@id, @string_handle)
233
- res.should == true
234
- end
235
-
236
- it 'returns false attempting to free unregistered handle' do
237
- res = dde_free_string_handle(@id, 12345)
238
- res.should == false
239
- end
240
-
241
- it 'keeps string accessible while references to it still exist' do
242
- # creates second handle to 'My String 2'
243
- string_handle_1 = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)
244
-
245
- dde_free_string_handle(@id, @string_handle)
246
- dde_query_string(@id, @string_handle).should == 'My String 2'
247
-
248
- dde_free_string_handle(@id, string_handle_1)
249
- dde_query_string(@id, @string_handle).should == nil
250
- end
251
-
252
- it 'makes string inaccessible once its last handle is freed' do
253
- dde_free_string_handle(@id, @string_handle)
254
- dde_query_string(@id, @string_handle).should == nil
255
- end
256
- end # describe '#dde_free_string_handle'
257
-
258
- describe '#dde_get_last_error' do
259
- spec{ use{ error_code = DdeGetLastError(@id) }}
260
- spec{ use{ error_code = dde_get_last_error(@id) }}
261
-
262
- it 'original API returns DMLERR_NO_ERROR if there is no last DDE error for given app instance' do
263
- DdeGetLastError(@id).should == DMLERR_NO_ERROR
264
- end
265
-
266
- it 'snake_case API returns nil if there is no last DDE error for given app instance' do
267
- dde_get_last_error(@id).should == nil
268
- end
269
-
270
- it 'returns error code of last DDE error for given app instance' do
271
- dde_name_service(@id, 1234, DNS_REGISTER )
272
- dde_get_last_error(@id).should == DMLERR_INVALIDPARAMETER
273
- end
274
- end # describe '#dde_get_last_error'
275
-
276
- end # context "with dde string handle to 'My String'"
277
- end # context 'after initialization:'
278
-
279
- context 'with synthetic DDE client/server' do
280
- before(:each){ setup_server }
281
- after(:each) { teardown_server}
282
-
283
- describe '#dde_name_service' do
284
- spec{ use{ success = dde_name_service(@server_id, @service_handle, cmd=DNS_UNREGISTER ) }}
285
- spec{ use{ success = DdeNameService(@server_id, @service_handle, reserved=0, cmd=DNS_UNREGISTER) }}
286
-
287
- it 'registers or unregisters the service names that DDE server supports' do
288
- pending 'Register/Unregister messages don`t show up in @server_calls :('
289
- success = dde_name_service(@server_id, @service_handle, DNS_REGISTER )
290
- success.should == true
291
- success = dde_name_service(@server_id, @service_handle, DNS_UNREGISTER )
292
- success.should == true
293
- end
294
- end # describe '#dde_name_service'
295
-
296
- describe '#dde_connect' do
297
- spec{ use{ @conv_handle = DdeConnect( instance_id=0, service=0, topic=0, context=nil) }}
298
- spec{ use{ @conv_handle = dde_connect( instance_id=0, service=0, topic=0, context=nil) }}
299
-
300
- it 'connects to existing DDE server (self in this case)' do
301
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
302
-
303
- @server_calls.first[0].should == 'XTYP_CONNECT'
304
- @server_calls.first[3].should == 'topic 2'
305
- @server_calls.first[4].should == 'service 2'
306
- @server_calls[1][0].should == 'XTYP_CONNECT_CONFIRM'
307
- @server_calls[1][3].should == 'topic 2'
308
- @server_calls[1][4].should == 'service 2'
309
- dde_disconnect(@server_conv).should == true
310
- dde_disconnect(@conv_handle).should == true
311
- end
312
-
313
- it 'connects to existing DDE server (from @client, NOT self)' do
314
- pending 'something is wrong when connecting to separate service instance, uninitialize fails'
315
- @conv_handle = dde_connect( @client_id, @service_handle, @topic_handle, context=nil)
316
- puts @conv_handle
317
- end
318
- end # describe '#dde_connect'
319
-
320
- describe '#dde_disconnect' do
321
- spec{ use{ success = DdeDisconnect(conversation_handle=0) }}
322
- spec{ use{ success = dde_disconnect(conversation_handle=0) }}
323
-
324
- it 'fails to disconnect if not valid conversation handle given' do
325
- dde_disconnect(12345).should == false
326
- end
327
-
328
- it 'disconnects from existing (self) DDE server' do
329
- conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
330
- dde_disconnect(conv_handle).should == true
331
- end
332
-
333
- it 'disconnects from existing (self) DDE server' do
334
- pending 'XTYP_DISCONNECT is not received by server callback (since we are disconnecting from self?)'
335
- conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
336
- dde_disconnect(conv_handle).should == true
337
- @server_calls.last[0].should == "XTYP_DISCONNECT"
338
- p @server_calls, @client_calls, @server_conv
339
- p ERRORS[dde_get_last_error(@server_id)]
340
- p ERRORS[dde_get_last_error(@client_id)]
341
- end
342
- end # describe '#dde_disconnect'
343
- end # context 'with synthetic DDE server'
344
-
345
- describe "#dde_client_transaction" do
346
- before(:each) do
347
- setup_server do |*args|
348
- @server_calls << extract_values(*args)
349
- @data_out, size = dde_get_data(args[5]) if args.first == XTYP_POKE || args.first == XTYP_EXECUTE
350
- DDE_FACK
351
- end
352
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
353
- @data_in = FFI::MemoryPointer.from_string POKE_STRING
354
- end
355
- after(:each) { teardown_server}
356
-
357
- spec{ use{ DdeClientTransaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
358
- spec{ use{ dde_client_transaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
359
-
360
-
361
- it "returns 0/nil if initiated transaction unsuccessful" do
362
- res = DdeClientTransaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
363
- res.should == 0 # wrong conversation handle
364
- res = dde_client_transaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
365
- res.should == nil # wrong conversation handle
366
- res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, CF_TEXT, XTYP_POKE, 1000, nil)
367
- res.should == nil # wrong item handle (cannot be NULL in XTYP_POKE transaction)
368
- @server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_false
369
- end
370
-
371
- it "original api is used by CLIENT to begins a data transaction with server" do
372
- res = DdeClientTransaction(@data_in, @data_in.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
373
- res.should == 1
374
- @server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_true
375
- @data_out.read_string.should == POKE_STRING.rstrip
376
- end
377
-
378
- it "snake_case api begins a data transaction between a client and a server" do
379
- res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, 0, XTYP_EXECUTE, 1000, nil)
380
- res.should be_true
381
- @server_calls.any? {|call| call[0] == 'XTYP_EXECUTE'}.should be_true
382
- @data_out.read_string.should == POKE_STRING.rstrip
383
- end
384
- end # describe dde_client_transaction
385
-
386
- describe '#dde_get_data' do
387
- after(:each) { teardown_server}
388
-
389
- spec{ use{ data_pointer, size = dde_get_data( data_handle = 123, max = 1073741823, offset = 0) }}
390
- spec{ use{ size = DdeGetData( data_handle = 123, nil, 0, 0) }} # returns dde data set size in bytes
391
- spec{ use{ size = DdeGetData( data_handle = 123, FFI::MemoryPointer.new(:char, 1024), max = 1024, offset = 0) }}
392
-
393
- it 'original API returns 0 if trying to address invalid dde data handle' do
394
- DdeGetData( data_handle = 123, nil, 0, 0).should == 0
395
- end
396
-
397
- it 'snake_case API returns [nil, 0] if trying to address invalid dde data handle' do
398
- data, size = dde_get_data( data_handle = 123, 3741823, 0)
399
- data.should == nil
400
- end
401
-
402
- it 'returns dde data if used inside dde callback block' do
403
- setup_server do |*args|
404
- @server_calls << extract_values(*args);
405
- if args[0] == XTYP_POKE
406
- data_handle = args[5]
407
- data, size = dde_get_data(data_handle)
408
- data.should be_an FFI::MemoryPointer
409
- data.read_string.should == POKE_STRING.rstrip
410
- size.should == 12
411
- DdeGetData(data_handle, nil, 0, 0).should == 12
412
- data = FFI::MemoryPointer.new(:char, 1024)
413
- DdeGetData(data_handle, data, data.size, 0).should == 12
414
- data.read_string.should == POKE_STRING.rstrip
415
- end
416
- DDE_FACK
417
- end
418
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
419
- str = FFI::MemoryPointer.from_string POKE_STRING
420
- dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
421
- end
422
-
423
- it 'dde data handle expires once transaction is finished (DDE_FACK)' do
424
- setup_server do |*args|
425
- @server_calls << extract_values(*args);
426
- DDE_FACK
427
- end
428
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
429
- str = FFI::MemoryPointer.from_string POKE_STRING
430
- dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
431
-
432
- # only inside callback block dde data handle is valid (while transaction still in progress)
433
- data, size = dde_get_data(@server_calls.last[5])
434
- data.should == nil
435
- size.should == 0
436
- end
437
-
438
- end # describe '#dde_get_data'
439
-
440
- describe "#dde_access_data" do
441
- after(:each) { teardown_server}
442
-
443
- spec{ use{ success = DdeAccessData(data_handle = 123, data_size=zero_id) }}
444
- spec{ use{ data_pointer, size = dde_access_data(data_handle = 123) }}
445
-
446
- it "provides access to the data in the specified DDE data handle (both inside and outside of callback)" do
447
- setup_server do |*args|
448
- @server_calls << extract_values(*args)
449
- if args[0] == XTYP_POKE
450
- data_handle = args[5]
451
- data, size = dde_access_data(data_handle)
452
- data.should be_kind_of FFI::Pointer
453
- data.read_string.should == POKE_STRING.rstrip
454
- size.should == 12
455
- buf = FFI::MemoryPointer.new(:int16)
456
- data = DdeAccessData(data_handle, buf)
457
- buf.get_int16(0).should == 12
458
- data.should be_kind_of FFI::Pointer
459
- data.read_string.should == POKE_STRING.rstrip
460
- dde_unaccess_data(data_handle)
461
- end
462
- DDE_FACK
463
- end
464
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
465
- str = FFI::MemoryPointer.from_string POKE_STRING
466
- dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
467
- end
468
-
469
- it 'dde data handle expires once transaction is finished (DDE_FACK)' do
470
- setup_server do |*args|
471
- @server_calls << extract_values(*args);
472
- DDE_FACK
473
- end
474
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
475
- str = FFI::MemoryPointer.from_string POKE_STRING
476
- dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
477
-
478
- # only inside callback block dde data handle is valid (while transaction still in progress)
479
- data, size = dde_access_data(@server_calls.last[5])
480
- data.should == nil
481
- size.should == 0
482
- end
483
- end # describe dde_access_data
484
-
485
- describe "#dde_unaccess_data" do
486
- spec{ use{ success = DdeUnaccessData(data_handle = 123) }}
487
- spec{ use{ success = dde_unaccess_data(data_handle = 123) }}
488
-
489
- it "returns 0/false if given invalid DDE data handle " do
490
- DdeUnaccessData(data_handle = 123).should == 0
491
- dde_unaccess_data(data_handle = 123).should == false
492
- end
493
-
494
- it "unaccesses a DDE data handle that was previously accessed" do
495
- setup_server do |*args|
496
- @server_calls << extract_values(*args);
497
- if args[0] == XTYP_POKE
498
- data_handle = args[5]
499
- dde_unaccess_data(data_handle).should == true
500
-
501
- data, size = dde_access_data(data_handle)
502
- data.should be_kind_of FFI::Pointer
503
- data.read_string.should == POKE_STRING.rstrip
504
-
505
- dde_unaccess_data(data_handle).should == true
506
- end
507
- DDE_FACK
508
- end
509
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
510
- str = FFI::MemoryPointer.from_string POKE_STRING
511
- dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
512
- end
513
-
514
- it 'dde data handle expires once transaction is finished (DDE_FACK)' do
515
- setup_server do |*args|
516
- @server_calls << extract_values(*args);
517
- DDE_FACK
518
- end
519
- @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
520
- str = FFI::MemoryPointer.from_string POKE_STRING
521
- dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
522
-
523
- # only inside callback block dde data handle is valid (while transaction still in progress)
524
- dde_unaccess_data(@server_calls.last[5]).should == false
525
- end
526
-
527
- end # describe dde_unaccess_data
528
- end
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'win/dde'
3
+
4
+ module WinDdeTest
5
+ include WinTest
6
+ include Win::Dde
7
+ include Win::Gui::Message
8
+
9
+ POKE_STRING = "Poke_string"
10
+
11
+ def dde_cmd
12
+ APPCLASS_STANDARD
13
+ end
14
+
15
+ def dde_callback
16
+ ->(*args){}
17
+ end
18
+
19
+ def zero_id
20
+ FFI::MemoryPointer.new(:long).write_long(0)
21
+ end
22
+
23
+ def buffer
24
+ FFI::MemoryPointer.new(:char, 1024)
25
+ end
26
+
27
+ def string_pointer
28
+ FFI::MemoryPointer.from_string("Pointer_string")
29
+ end
30
+
31
+ def extract_values(*args)
32
+ type, format, conv, hsz1, hsz2, data, data1, data2 = *args
33
+ @server_conv = conv
34
+ [Win::Dde::TYPES[type], format, conv,
35
+ dde_query_string(@client_id, hsz1),
36
+ dde_query_string(@client_id, hsz2),
37
+ data, data1, data2]
38
+ end
39
+
40
+ def setup_server(&server_block)
41
+ @client_calls = []
42
+ @server_calls = []
43
+ @client_id, st = dde_initialize(APPCLASS_STANDARD) {|*args| @client_calls << extract_values(*args); DDE_FACK}
44
+ @server_id, st = dde_initialize(APPCLASS_STANDARD,
45
+ &server_block || proc {|*args| @server_calls << extract_values(*args); DDE_FACK} )
46
+ @service_handle = dde_create_string_handle(@server_id, 'service 2', CP_WINANSI)
47
+ @topic_handle = dde_create_string_handle(@server_id, 'topic 2', CP_WINANSI)
48
+ dde_name_service(@server_id, @service_handle, DNS_REGISTER)
49
+ end
50
+
51
+ def teardown_server
52
+ if @print
53
+ p @server_calls, @client_calls
54
+ p @server_conv
55
+ p ERRORS[dde_get_last_error(@server_id)]
56
+ p ERRORS[dde_get_last_error(@client_id)]
57
+ @print = nil
58
+ end
59
+ dde_name_service(@server_id, @service_handle, DNS_UNREGISTER) if @server_id && @service_handle
60
+ dde_free_string_handle(@server_id, @service_handle) if @server_id && @service_handle
61
+ dde_free_string_handle(@server_id, @topic_handle) if @server_id && @topic_handle
62
+ dde_uninitialize(@server_id) if @server_id
63
+ dde_uninitialize(@client_id) if @client_id
64
+ if @conv_handle
65
+ dde_disconnect(@conv_handle)
66
+ @conv_handle = nil
67
+ end
68
+ @data = nil
69
+ end
70
+
71
+ describe Win::Dde, ' contains a set of pre-defined Windows API functions' do
72
+
73
+ describe '#register_clipboard_format' do
74
+ spec{ use{ RegisterClipboardFormat(format_name = "XlTable") }}
75
+ spec{ use{ register_clipboard_format(format_name = "XlTable") }}
76
+
77
+ it 'returns format id (int) if successfully registered format' do
78
+ id = register_clipboard_format("XlTable")
79
+ id.should_not == 0
80
+ id.should_not == nil
81
+ end
82
+
83
+ it 'returns same format id for already registered format' do
84
+ id1 = register_clipboard_format("XlTable")
85
+ id2 = register_clipboard_format("XlTable")
86
+ id1.should == id2
87
+ end
88
+
89
+ it 'returns nil if not able to register format' do
90
+ register_clipboard_format("").should == nil
91
+ end
92
+ end
93
+
94
+ describe '#dde_initialize' do
95
+ after(:each) {dde_uninitialize(@id) if @id}
96
+
97
+ spec{ use{ status = DdeInitialize( id = zero_id, dde_callback, dde_cmd, unused=0); @id = id.read_long}}
98
+ spec{ use{ @id, status = dde_initialize(@id=0, dde_cmd, &dde_callback) }}
99
+
100
+ it 'with zero instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
101
+ @id, status = dde_initialize(0, APPCLASS_STANDARD) {|*args| }
102
+ @id.should be_an Integer
103
+ @id.should_not == 0
104
+ status.should == DMLERR_NO_ERROR
105
+ end
106
+
107
+ it 'with nil instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
108
+ @id, status = dde_initialize(nil, APPCLASS_STANDARD) {|*args| }
109
+ @id.should be_an Integer
110
+ @id.should_not == 0
111
+ status.should == DMLERR_NO_ERROR
112
+ end
113
+
114
+ it 'with omitted instance_id, returns integer id and DMLERR_NO_ERROR if initialization successful' do
115
+ @id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
116
+ @id.should be_an Integer
117
+ @id.should_not == 0
118
+ status.should == DMLERR_NO_ERROR
119
+ end
120
+
121
+ it 'returns error status if initialization unsuccessful' do
122
+ @id, status = dde_initialize(12345, APPCLASS_STANDARD) {|*args| }
123
+ status.should == DMLERR_INVALIDPARAMETER
124
+ @id.should == nil
125
+ end
126
+
127
+ it 'is able to reinitialize with correct id' do
128
+ @id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }
129
+ new_id, status = dde_initialize(@id, APPCLASS_STANDARD) {|*args| }
130
+ status.should == DMLERR_NO_ERROR
131
+ new_id.should == @id
132
+ end
133
+ end # describe 'dde_initialize'
134
+
135
+ context 'after initialization:' do
136
+ before(:each) {@id, status = dde_initialize(APPCLASS_STANDARD) {|*args| }}
137
+ after(:each) {dde_uninitialize(@id) if @id}
138
+
139
+ describe '#dde_uninitialize' do
140
+
141
+ spec{ use{ status = DdeUninitialize( @id ) }}
142
+ spec{ use{ id, status = dde_uninitialize( @id) }}
143
+
144
+ it 'returns true if uninitialization successful' do
145
+ res = dde_uninitialize(@id)
146
+ res.should == true
147
+ end
148
+
149
+ it 'returns false if initialization unsuccessful' do
150
+ res = dde_uninitialize(12345)
151
+ res.should == false
152
+ end
153
+ end # describe '#dde_uninitialize'
154
+
155
+ describe '#dde_create_string_handle' do
156
+ after(:each) {dde_free_string_handle(@id, @string_handle) if @string_handle}
157
+
158
+ spec{ use{ @string_handle = DdeCreateStringHandle(id=0, string_pointer, code_page_id=CP_WINANSI) }}
159
+ spec{ use{ @string_handle = dde_create_string_handle(id=0, string='Any String', code_page_id=CP_WINANSI)}}
160
+
161
+ it 'returns nonzero Integer handle to a string (passable to other DDEML functions)' do
162
+ @string_handle = dde_create_string_handle(@id, 'My String', CP_WINANSI)
163
+ @string_handle.should be_an Integer
164
+ @string_handle.should_not == 0
165
+ end
166
+
167
+ it 'creates handle even if code_page is omitted' do
168
+ @string_handle = dde_create_string_handle(@id, 'My String')
169
+ @string_handle.should be_an Integer
170
+ @string_handle.should_not == 0
171
+ end
172
+
173
+ it 'creating two handles for the SAME string (inside one instance) USUALLY returns same handle' do
174
+ @string_handle = dde_create_string_handle(@id, 'My String')
175
+ 10.times do
176
+ string_handle1 = dde_create_string_handle(@id, 'My String')
177
+ string_handle1.should == @string_handle
178
+ dde_free_string_handle(@id, string_handle1)
179
+ end
180
+ end
181
+
182
+ it 'created different handles for two different strings ' do
183
+ @string_handle = dde_create_string_handle(@id, 'My String')
184
+ string_handle1 = dde_create_string_handle(@id, 'My String1')
185
+ string_handle1.should_not == @string_handle
186
+ dde_free_string_handle(@id, string_handle1)
187
+ end
188
+
189
+ it 'returns nil if unable to register handle to a string' do
190
+ @string_handle = dde_create_string_handle(@id, "", CP_WINANSI)
191
+ @string_handle.should == nil
192
+ end
193
+ end # describe '#dde_create_string_handle'
194
+
195
+ context "with dde string handle to 'My String 2'" do
196
+ before(:each) {@string_handle = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)}
197
+ after(:each) {dde_free_string_handle(@id, @string_handle)}
198
+
199
+ describe '#dde_query_string' do
200
+
201
+ spec{ use{ string = DdeQueryString(@id, @string_handle, buffer, buffer.size, code_page=CP_WINANSI)}}
202
+ spec{ use{ string = dde_query_string(@id, @string_handle, code_page=CP_WINANSI )}}
203
+
204
+ it 'retrieves string that given string handle refers to' do
205
+ num_chars = DdeQueryString(@id, @string_handle, buf = buffer, buf.size, CP_WINANSI)
206
+ num_chars.should == 11
207
+ buf.read_string.should == 'My String 2'
208
+ end
209
+
210
+ it 'retrieves string that given string handle refers to' do
211
+ string = dde_query_string(@id, @string_handle, CP_WINANSI)
212
+ string.should == 'My String 2'
213
+ end
214
+
215
+ it 'retrieves string even if code_page is omitted' do
216
+ string = dde_query_string(@id, @string_handle)
217
+ string.should == 'My String 2'
218
+ end
219
+
220
+ it 'returns nil attempting to retrieve invalid handle' do
221
+ string = dde_query_string(@id, 12345)
222
+ string.should == nil
223
+ end
224
+ end # describe '#dde_query_string'
225
+
226
+ describe '#dde_free_string_handle' do
227
+
228
+ spec{ use{ success = DdeFreeStringHandle( @id, @string_handle)}}
229
+ spec{ use{ success = dde_free_string_handle( @id, @string_handle )}}
230
+
231
+ it 'returns true when freeing string handle registered with DDEML' do
232
+ res = dde_free_string_handle(@id, @string_handle)
233
+ res.should == true
234
+ end
235
+
236
+ it 'returns false attempting to free unregistered handle' do
237
+ res = dde_free_string_handle(@id, 12345)
238
+ res.should == false
239
+ end
240
+
241
+ it 'keeps string accessible while references to it still exist' do
242
+ # creates second handle to 'My String 2'
243
+ string_handle_1 = dde_create_string_handle(@id, 'My String 2', CP_WINANSI)
244
+
245
+ dde_free_string_handle(@id, @string_handle)
246
+ dde_query_string(@id, @string_handle).should == 'My String 2'
247
+
248
+ dde_free_string_handle(@id, string_handle_1)
249
+ dde_query_string(@id, @string_handle).should == nil
250
+ end
251
+
252
+ it 'makes string inaccessible once its last handle is freed' do
253
+ dde_free_string_handle(@id, @string_handle)
254
+ dde_query_string(@id, @string_handle).should == nil
255
+ end
256
+ end # describe '#dde_free_string_handle'
257
+
258
+ describe '#dde_get_last_error' do
259
+ spec{ use{ error_code = DdeGetLastError(@id) }}
260
+ spec{ use{ error_code = dde_get_last_error(@id) }}
261
+
262
+ it 'original API returns DMLERR_NO_ERROR if there is no last DDE error for given app instance' do
263
+ DdeGetLastError(@id).should == DMLERR_NO_ERROR
264
+ end
265
+
266
+ it 'snake_case API returns nil if there is no last DDE error for given app instance' do
267
+ dde_get_last_error(@id).should == nil
268
+ end
269
+
270
+ it 'returns error code of last DDE error for given app instance' do
271
+ dde_name_service(@id, 1234, DNS_REGISTER )
272
+ dde_get_last_error(@id).should == DMLERR_INVALIDPARAMETER
273
+ end
274
+ end # describe '#dde_get_last_error'
275
+
276
+ end # context "with dde string handle to 'My String'"
277
+ end # context 'after initialization:'
278
+
279
+ context 'with synthetic DDE client/server' do
280
+ before(:each){ setup_server }
281
+ after(:each) { teardown_server}
282
+
283
+ describe '#dde_name_service' do
284
+ spec{ use{ success = dde_name_service(@server_id, @service_handle, cmd=DNS_UNREGISTER ) }}
285
+ spec{ use{ success = DdeNameService(@server_id, @service_handle, reserved=0, cmd=DNS_UNREGISTER) }}
286
+
287
+ it 'registers or unregisters the service names that DDE server supports' do
288
+ pending 'Register/Unregister messages don`t show up in @server_calls :('
289
+ success = dde_name_service(@server_id, @service_handle, DNS_REGISTER )
290
+ success.should == true
291
+ success = dde_name_service(@server_id, @service_handle, DNS_UNREGISTER )
292
+ success.should == true
293
+ end
294
+ end # describe '#dde_name_service'
295
+
296
+ describe '#dde_connect' do
297
+ spec{ use{ @conv_handle = DdeConnect( instance_id=0, service=0, topic=0, context=nil) }}
298
+ spec{ use{ @conv_handle = dde_connect( instance_id=0, service=0, topic=0, context=nil) }}
299
+
300
+ it 'connects to existing DDE server (self in this case)' do
301
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
302
+
303
+ @server_calls.first[0].should == 'XTYP_CONNECT'
304
+ @server_calls.first[3].should == 'topic 2'
305
+ @server_calls.first[4].should == 'service 2'
306
+ @server_calls[1][0].should == 'XTYP_CONNECT_CONFIRM'
307
+ @server_calls[1][3].should == 'topic 2'
308
+ @server_calls[1][4].should == 'service 2'
309
+ dde_disconnect(@server_conv).should == true
310
+ dde_disconnect(@conv_handle).should == true
311
+ end
312
+
313
+ it 'connects to existing DDE server (from @client, NOT self)' do
314
+ pending 'something is wrong when connecting to separate service instance, uninitialize fails'
315
+ @conv_handle = dde_connect( @client_id, @service_handle, @topic_handle, context=nil)
316
+ puts @conv_handle
317
+ end
318
+ end # describe '#dde_connect'
319
+
320
+ describe '#dde_disconnect' do
321
+ spec{ use{ success = DdeDisconnect(conversation_handle=0) }}
322
+ spec{ use{ success = dde_disconnect(conversation_handle=0) }}
323
+
324
+ it 'fails to disconnect if not valid conversation handle given' do
325
+ dde_disconnect(12345).should == false
326
+ end
327
+
328
+ it 'disconnects from existing (self) DDE server' do
329
+ conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
330
+ dde_disconnect(conv_handle).should == true
331
+ end
332
+
333
+ it 'disconnects from existing (self) DDE server' do
334
+ pending 'XTYP_DISCONNECT is not received by server callback (since we are disconnecting from self?)'
335
+ conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
336
+ dde_disconnect(conv_handle).should == true
337
+ @server_calls.last[0].should == "XTYP_DISCONNECT"
338
+ p @server_calls, @client_calls, @server_conv
339
+ p ERRORS[dde_get_last_error(@server_id)]
340
+ p ERRORS[dde_get_last_error(@client_id)]
341
+ end
342
+ end # describe '#dde_disconnect'
343
+ end # context 'with synthetic DDE server'
344
+
345
+ describe "#dde_client_transaction" do
346
+ before(:each) do
347
+ setup_server do |*args|
348
+ @server_calls << extract_values(*args)
349
+ @data_out, size = dde_get_data(args[5]) if args.first == XTYP_POKE || args.first == XTYP_EXECUTE
350
+ DDE_FACK
351
+ end
352
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
353
+ @data_in = FFI::MemoryPointer.from_string POKE_STRING
354
+ end
355
+ after(:each) { teardown_server}
356
+
357
+ spec{ use{ DdeClientTransaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
358
+ spec{ use{ dde_client_transaction(data=nil, size=0, conv=0, item=0, format=0, type=0, timeout=0, result=nil) }}
359
+
360
+
361
+ it "returns 0/nil if initiated transaction unsuccessful" do
362
+ res = DdeClientTransaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
363
+ res.should == 0 # wrong conversation handle
364
+ res = dde_client_transaction(@data_in, @data_in.size, 1234, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
365
+ res.should == nil # wrong conversation handle
366
+ res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, CF_TEXT, XTYP_POKE, 1000, nil)
367
+ res.should == nil # wrong item handle (cannot be NULL in XTYP_POKE transaction)
368
+ @server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_false
369
+ end
370
+
371
+ it "original api is used by CLIENT to begins a data transaction with server" do
372
+ res = DdeClientTransaction(@data_in, @data_in.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
373
+ res.should == 1
374
+ @server_calls.any? {|call| call[0] == 'XTYP_POKE'}.should be_true
375
+ @data_out.read_string.should == POKE_STRING.rstrip
376
+ end
377
+
378
+ it "snake_case api begins a data transaction between a client and a server" do
379
+ res = dde_client_transaction(@data_in, @data_in.size, @conv_handle, 0, 0, XTYP_EXECUTE, 1000, nil)
380
+ res.should be_true
381
+ @server_calls.any? {|call| call[0] == 'XTYP_EXECUTE'}.should be_true
382
+ @data_out.read_string.should == POKE_STRING.rstrip
383
+ end
384
+ end # describe dde_client_transaction
385
+
386
+ describe '#dde_get_data' do
387
+ after(:each) { teardown_server}
388
+
389
+ spec{ use{ data_pointer, size = dde_get_data( data_handle = 123, max = 1073741823, offset = 0) }}
390
+ spec{ use{ size = DdeGetData( data_handle = 123, nil, 0, 0) }} # returns dde data set size in bytes
391
+ spec{ use{ size = DdeGetData( data_handle = 123, FFI::MemoryPointer.new(:char, 1024), max = 1024, offset = 0) }}
392
+
393
+ it 'original API returns 0 if trying to address invalid dde data handle' do
394
+ DdeGetData( data_handle = 123, nil, 0, 0).should == 0
395
+ end
396
+
397
+ it 'snake_case API returns [nil, 0] if trying to address invalid dde data handle' do
398
+ data, size = dde_get_data( data_handle = 123, 3741823, 0)
399
+ data.should == nil
400
+ end
401
+
402
+ it 'returns dde data if used inside dde callback block' do
403
+ setup_server do |*args|
404
+ @server_calls << extract_values(*args);
405
+ if args[0] == XTYP_POKE
406
+ data_handle = args[5]
407
+ data, size = dde_get_data(data_handle)
408
+ data.should be_an FFI::MemoryPointer
409
+ data.read_string.should == POKE_STRING.rstrip
410
+ size.should == 12
411
+ DdeGetData(data_handle, nil, 0, 0).should == 12
412
+ data = FFI::MemoryPointer.new(:char, 1024)
413
+ DdeGetData(data_handle, data, data.size, 0).should == 12
414
+ data.read_string.should == POKE_STRING.rstrip
415
+ end
416
+ DDE_FACK
417
+ end
418
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
419
+ str = FFI::MemoryPointer.from_string POKE_STRING
420
+ dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
421
+ end
422
+
423
+ it 'dde data handle expires once transaction is finished (DDE_FACK)' do
424
+ setup_server do |*args|
425
+ @server_calls << extract_values(*args);
426
+ DDE_FACK
427
+ end
428
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
429
+ str = FFI::MemoryPointer.from_string POKE_STRING
430
+ dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
431
+
432
+ # only inside callback block dde data handle is valid (while transaction still in progress)
433
+ data, size = dde_get_data(@server_calls.last[5])
434
+ data.should == nil
435
+ size.should == 0
436
+ end
437
+
438
+ end # describe '#dde_get_data'
439
+
440
+ describe "#dde_access_data" do
441
+ after(:each) { teardown_server}
442
+
443
+ spec{ use{ success = DdeAccessData(data_handle = 123, data_size=zero_id) }}
444
+ spec{ use{ data_pointer, size = dde_access_data(data_handle = 123) }}
445
+
446
+ it "provides access to the data in the specified DDE data handle (both inside and outside of callback)" do
447
+ setup_server do |*args|
448
+ @server_calls << extract_values(*args)
449
+ if args[0] == XTYP_POKE
450
+ data_handle = args[5]
451
+ data, size = dde_access_data(data_handle)
452
+ data.should be_kind_of FFI::Pointer
453
+ data.read_string.should == POKE_STRING.rstrip
454
+ size.should == 12
455
+ buf = FFI::MemoryPointer.new(:int16)
456
+ data = DdeAccessData(data_handle, buf)
457
+ buf.get_int16(0).should == 12
458
+ data.should be_kind_of FFI::Pointer
459
+ data.read_string.should == POKE_STRING.rstrip
460
+ dde_unaccess_data(data_handle)
461
+ end
462
+ DDE_FACK
463
+ end
464
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
465
+ str = FFI::MemoryPointer.from_string POKE_STRING
466
+ dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
467
+ end
468
+
469
+ it 'dde data handle expires once transaction is finished (DDE_FACK)' do
470
+ setup_server do |*args|
471
+ @server_calls << extract_values(*args);
472
+ DDE_FACK
473
+ end
474
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
475
+ str = FFI::MemoryPointer.from_string POKE_STRING
476
+ dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
477
+
478
+ # only inside callback block dde data handle is valid (while transaction still in progress)
479
+ data, size = dde_access_data(@server_calls.last[5])
480
+ data.should == nil
481
+ size.should == 0
482
+ end
483
+ end # describe dde_access_data
484
+
485
+ describe "#dde_unaccess_data" do
486
+ spec{ use{ success = DdeUnaccessData(data_handle = 123) }}
487
+ spec{ use{ success = dde_unaccess_data(data_handle = 123) }}
488
+
489
+ it "returns 0/false if given invalid DDE data handle " do
490
+ DdeUnaccessData(data_handle = 123).should == 0
491
+ dde_unaccess_data(data_handle = 123).should == false
492
+ end
493
+
494
+ it "unaccesses a DDE data handle that was previously accessed" do
495
+ setup_server do |*args|
496
+ @server_calls << extract_values(*args);
497
+ if args[0] == XTYP_POKE
498
+ data_handle = args[5]
499
+ dde_unaccess_data(data_handle).should == true
500
+
501
+ data, size = dde_access_data(data_handle)
502
+ data.should be_kind_of FFI::Pointer
503
+ data.read_string.should == POKE_STRING.rstrip
504
+
505
+ dde_unaccess_data(data_handle).should == true
506
+ end
507
+ DDE_FACK
508
+ end
509
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
510
+ str = FFI::MemoryPointer.from_string POKE_STRING
511
+ dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
512
+ end
513
+
514
+ it 'dde data handle expires once transaction is finished (DDE_FACK)' do
515
+ setup_server do |*args|
516
+ @server_calls << extract_values(*args);
517
+ DDE_FACK
518
+ end
519
+ @conv_handle = dde_connect( @server_id, @service_handle, @topic_handle, context=nil)
520
+ str = FFI::MemoryPointer.from_string POKE_STRING
521
+ dde_client_transaction(str, str.size, @conv_handle, @topic_handle, CF_TEXT, XTYP_POKE, 1000, nil)
522
+
523
+ # only inside callback block dde data handle is valid (while transaction still in progress)
524
+ dde_unaccess_data(@server_calls.last[5]).should == false
525
+ end
526
+
527
+ end # describe dde_unaccess_data
528
+ end
529
529
  end