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/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