launchpad 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,429 @@
1
+ require 'helper'
2
+
3
+ class TestDevice < Test::Unit::TestCase
4
+
5
+ CONTROL_BUTTONS = {
6
+ :up => 0x68,
7
+ :down => 0x69,
8
+ :left => 0x6A,
9
+ :right => 0x6B,
10
+ :session => 0x6C,
11
+ :user1 => 0x6D,
12
+ :user2 => 0x6E,
13
+ :mixer => 0x6F
14
+ }
15
+ SCENE_BUTTONS = {
16
+ :scene1 => 0x08,
17
+ :scene2 => 0x18,
18
+ :scene3 => 0x28,
19
+ :scene4 => 0x38,
20
+ :scene5 => 0x48,
21
+ :scene6 => 0x58,
22
+ :scene7 => 0x68,
23
+ :scene8 => 0x78
24
+ }
25
+ COLORS = {
26
+ nil => 0, 0 => 0, :off => 0,
27
+ 1 => 1, :lo => 1, :low => 1,
28
+ 2 => 2, :med => 2, :medium => 2,
29
+ 3 => 3, :hi => 3, :high => 3
30
+ }
31
+ STATES = {
32
+ :down => 127,
33
+ :up => 0
34
+ }
35
+
36
+ def expects_output(device, *args)
37
+ device.instance_variable_get('@output').expects(:write).with([{:message => args, :timestamp => 0}])
38
+ end
39
+
40
+ def stub_input(device, *args)
41
+ device.instance_variable_get('@input').stubs(:read).returns(args)
42
+ end
43
+
44
+ context 'initializer' do
45
+
46
+ should 'try to initialize both input and output when not specified' do
47
+ Portmidi.expects(:input_devices).returns(mock_devices)
48
+ Portmidi.expects(:output_devices).returns(mock_devices)
49
+ d = Launchpad::Device.new
50
+ assert_not_nil d.instance_variable_get('@input')
51
+ assert_not_nil d.instance_variable_get('@output')
52
+ end
53
+
54
+ should 'not try to initialize input when set to false' do
55
+ Portmidi.expects(:input_devices).never
56
+ d = Launchpad::Device.new(:input => false)
57
+ assert_nil d.instance_variable_get('@input')
58
+ assert_not_nil d.instance_variable_get('@output')
59
+ end
60
+
61
+ should 'not try to initialize output when set to false' do
62
+ Portmidi.expects(:output_devices).never
63
+ d = Launchpad::Device.new(:output => false)
64
+ assert_not_nil d.instance_variable_get('@input')
65
+ assert_nil d.instance_variable_get('@output')
66
+ end
67
+
68
+ should 'not try to initialize any of both when set to false' do
69
+ Portmidi.expects(:input_devices).never
70
+ Portmidi.expects(:output_devices).never
71
+ d = Launchpad::Device.new(:input => false, :output => false)
72
+ assert_nil d.instance_variable_get('@input')
73
+ assert_nil d.instance_variable_get('@output')
74
+ end
75
+
76
+ should 'initialize the correct input output devices' do
77
+ Portmidi.stubs(:input_devices).returns(mock_devices(:id => 4, :name => 'Launchpad Name'))
78
+ Portmidi.stubs(:output_devices).returns(mock_devices(:id => 5, :name => 'Launchpad Name'))
79
+ d = Launchpad::Device.new(:device_name => 'Launchpad Name')
80
+ assert_equal Portmidi::Input, (input = d.instance_variable_get('@input')).class
81
+ assert_equal 4, input.device_id
82
+ assert_equal Portmidi::Output, (output = d.instance_variable_get('@output')).class
83
+ assert_equal 5, output.device_id
84
+ end
85
+
86
+ should 'raise NoSuchDeviceError when requested input device does not exist' do
87
+ assert_raise Launchpad::NoSuchDeviceError do
88
+ Portmidi.stubs(:input_devices).returns(mock_devices(:name => 'Launchpad Input'))
89
+ Launchpad::Device.new
90
+ end
91
+ end
92
+
93
+ should 'raise NoSuchDeviceError when requested output device does not exist' do
94
+ assert_raise Launchpad::NoSuchDeviceError do
95
+ Portmidi.stubs(:output_devices).returns(mock_devices(:name => 'Launchpad Output'))
96
+ Launchpad::Device.new
97
+ end
98
+ end
99
+
100
+ should 'raise DeviceBusyError when requested input device is busy' do
101
+ assert_raise Launchpad::DeviceBusyError do
102
+ Portmidi::Input.stubs(:new).raises(RuntimeError)
103
+ Launchpad::Device.new
104
+ end
105
+ end
106
+
107
+ should 'raise DeviceBusyError when requested output device is busy' do
108
+ assert_raise Launchpad::DeviceBusyError do
109
+ Portmidi::Output.stubs(:new).raises(RuntimeError)
110
+ Launchpad::Device.new
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ {
117
+ :reset => [0xB0, 0x00, 0x00],
118
+ :flashing_on => [0xB0, 0x00, 0x20],
119
+ :flashing_off => [0xB0, 0x00, 0x21],
120
+ :flashing_auto => [0xB0, 0x00, 0x28]
121
+ }.each do |method, codes|
122
+ context method do
123
+
124
+ should 'raise NoOutputAllowedError when not initialized with output' do
125
+ assert_raise Launchpad::NoOutputAllowedError do
126
+ Launchpad::Device.new(:output => false).send(method)
127
+ end
128
+ end
129
+
130
+ should "send #{codes.inspect}" do
131
+ d = Launchpad::Device.new
132
+ expects_output(d, *codes)
133
+ d.send(method)
134
+ end
135
+
136
+ end
137
+ end
138
+
139
+ context 'test_leds' do
140
+
141
+ should 'raise NoOutputAllowedError when not initialized with output' do
142
+ assert_raise Launchpad::NoOutputAllowedError do
143
+ Launchpad::Device.new(:output => false).test_leds
144
+ end
145
+ end
146
+
147
+ context 'initialized with output' do
148
+
149
+ setup do
150
+ @device = Launchpad::Device.new(:input => false)
151
+ end
152
+
153
+ should 'return nil' do
154
+ assert_nil @device.test_leds
155
+ end
156
+
157
+ COLORS.merge(nil => 3).each do |name, value|
158
+ if value == 0
159
+ should "send 0xB0, 0x00, 0x00 when given #{name}" do
160
+ expects_output(@device, 0xB0, 0x00, 0x00)
161
+ @device.test_leds(value)
162
+ end
163
+ else
164
+ should "send 0xB0, 0x00, 0x7C + #{value} when given #{name}" do
165
+ d = Launchpad::Device.new
166
+ expects_output(@device, 0xB0, 0x00, 0x7C + value)
167
+ value.nil? ? @device.test_leds : @device.test_leds(value)
168
+ end
169
+ end
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
176
+ context 'change' do
177
+
178
+ should 'raise NoOutputAllowedError when not initialized with output' do
179
+ assert_raise Launchpad::NoOutputAllowedError do
180
+ Launchpad::Device.new(:output => false).change(:up)
181
+ end
182
+ end
183
+
184
+ context 'initialized with output' do
185
+
186
+ setup do
187
+ @device = Launchpad::Device.new(:input => false)
188
+ end
189
+
190
+ should 'return nil' do
191
+ assert_nil @device.change(:up)
192
+ end
193
+
194
+ context 'control buttons' do
195
+ CONTROL_BUTTONS.each do |type, value|
196
+ should "send 0xB0, #{value}, 12 when given #{type}" do
197
+ expects_output(@device, 0xB0, value, 12)
198
+ @device.change(type)
199
+ end
200
+ end
201
+ end
202
+
203
+ context 'scene buttons' do
204
+ SCENE_BUTTONS.each do |type, value|
205
+ should "send 0x90, #{value}, 12 when given #{type}" do
206
+ expects_output(@device, 0x90, value, 12)
207
+ @device.change(type)
208
+ end
209
+ end
210
+ end
211
+
212
+ context 'grid buttons' do
213
+ 8.times do |x|
214
+ 8.times do |y|
215
+ should "send 0x90, #{16 * y + x}, 12 when given :grid, :x => #{x}, :y => #{y}" do
216
+ expects_output(@device, 0x90, 16 * y + x, 12)
217
+ @device.change(:grid, :x => x, :y => y)
218
+ end
219
+ end
220
+ end
221
+
222
+ should 'raise NoValidGridCoordinatesError if x is not specified' do
223
+ assert_raise Launchpad::NoValidGridCoordinatesError do
224
+ @device.change(:grid, :y => 1)
225
+ end
226
+ end
227
+
228
+ should 'raise NoValidGridCoordinatesError if x is below 0' do
229
+ assert_raise Launchpad::NoValidGridCoordinatesError do
230
+ @device.change(:grid, :x => -1, :y => 1)
231
+ end
232
+ end
233
+
234
+ should 'raise NoValidGridCoordinatesError if x is above 7' do
235
+ assert_raise Launchpad::NoValidGridCoordinatesError do
236
+ @device.change(:grid, :x => 8, :y => 1)
237
+ end
238
+ end
239
+
240
+ should 'raise NoValidGridCoordinatesError if y is not specified' do
241
+ assert_raise Launchpad::NoValidGridCoordinatesError do
242
+ @device.change(:grid, :x => 1)
243
+ end
244
+ end
245
+
246
+ should 'raise NoValidGridCoordinatesError if y is below 0' do
247
+ assert_raise Launchpad::NoValidGridCoordinatesError do
248
+ @device.change(:grid, :x => 1, :y => -1)
249
+ end
250
+ end
251
+
252
+ should 'raise NoValidGridCoordinatesError if y is above 7' do
253
+ assert_raise Launchpad::NoValidGridCoordinatesError do
254
+ @device.change(:grid, :x => 1, :y => 8)
255
+ end
256
+ end
257
+
258
+ end
259
+
260
+ context 'colors' do
261
+ COLORS.each do |red_key, red_value|
262
+ COLORS.each do |green_key, green_value|
263
+ should "send 0x90, 0, #{16 * green_value + red_value + 12} when given :red => #{red_key}, :green => #{green_key}" do
264
+ expects_output(@device, 0x90, 0, 16 * green_value + red_value + 12)
265
+ @device.change(:grid, :x => 0, :y => 0, :red => red_key, :green => green_key)
266
+ end
267
+ end
268
+ end
269
+
270
+ should 'raise NoValidBrightnessError if red is below 0' do
271
+ assert_raise Launchpad::NoValidBrightnessError do
272
+ @device.change(:grid, :x => 0, :y => 0, :red => -1)
273
+ end
274
+ end
275
+
276
+ should 'raise NoValidBrightnessError if red is above 3' do
277
+ assert_raise Launchpad::NoValidBrightnessError do
278
+ @device.change(:grid, :x => 0, :y => 0, :red => 4)
279
+ end
280
+ end
281
+
282
+ should 'raise NoValidBrightnessError if red is an unknown symbol' do
283
+ assert_raise Launchpad::NoValidBrightnessError do
284
+ @device.change(:grid, :x => 0, :y => 0, :red => :unknown)
285
+ end
286
+ end
287
+
288
+ should 'raise NoValidBrightnessError if green is below 0' do
289
+ assert_raise Launchpad::NoValidBrightnessError do
290
+ @device.change(:grid, :x => 0, :y => 0, :green => -1)
291
+ end
292
+ end
293
+
294
+ should 'raise NoValidBrightnessError if green is above 3' do
295
+ assert_raise Launchpad::NoValidBrightnessError do
296
+ @device.change(:grid, :x => 0, :y => 0, :green => 4)
297
+ end
298
+ end
299
+
300
+ should 'raise NoValidBrightnessError if green is an unknown symbol' do
301
+ assert_raise Launchpad::NoValidBrightnessError do
302
+ @device.change(:grid, :x => 0, :y => 0, :green => :unknown)
303
+ end
304
+ end
305
+
306
+ end
307
+
308
+ context 'mode' do
309
+
310
+ should 'send color + 12 when nothing given' do
311
+ expects_output(@device, 0x90, 0, 12)
312
+ @device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0)
313
+ end
314
+
315
+ should 'send color + 12 when given :normal' do
316
+ expects_output(@device, 0x90, 0, 12)
317
+ @device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0, :mode => :normal)
318
+ end
319
+
320
+ should 'send color + 8 when given :flashing' do
321
+ expects_output(@device, 0x90, 0, 8)
322
+ @device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0, :mode => :flashing)
323
+ end
324
+
325
+ should 'send color when given :buffering' do
326
+ expects_output(@device, 0x90, 0, 0)
327
+ @device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0, :mode => :buffering)
328
+ end
329
+
330
+ end
331
+
332
+ end
333
+
334
+ end
335
+
336
+ context 'change_all' do
337
+
338
+ should 'raise NoOutputAllowedError when not initialized with output' do
339
+ assert_raise Launchpad::NoOutputAllowedError do
340
+ Launchpad::Device.new(:output => false).change_all
341
+ end
342
+ end
343
+
344
+ context 'initialized with output' do
345
+
346
+ setup do
347
+ @device = Launchpad::Device.new(:input => false)
348
+ end
349
+
350
+ should 'return nil' do
351
+ assert_nil @device.change_all([0])
352
+ end
353
+
354
+ should 'fill colors with 0, set grid 0,0 to 0 and flush colors' do
355
+ expects_output(@device, 0x90, 0, 0)
356
+ 20.times {|i| expects_output(@device, 0x92, 17, 17)}
357
+ 20.times {|i| expects_output(@device, 0x92, 12, 12)}
358
+ @device.change_all([5] * 40)
359
+ end
360
+
361
+ should 'cut off exceeding colors, set grid 0,0 to 0 and flush colors' do
362
+ expects_output(@device, 0x90, 0, 0)
363
+ 40.times {|i| expects_output(@device, 0x92, 17, 17)}
364
+ @device.change_all([5] * 100)
365
+ end
366
+
367
+ end
368
+
369
+ end
370
+
371
+ context 'read_pending_actions' do
372
+
373
+ should 'raise NoInputAllowedError when not initialized with input' do
374
+ assert_raise Launchpad::NoInputAllowedError do
375
+ Launchpad::Device.new(:input => false).read_pending_actions
376
+ end
377
+ end
378
+
379
+ context 'initialized with input' do
380
+
381
+ setup do
382
+ @device = Launchpad::Device.new(:output => false)
383
+ end
384
+
385
+ context 'control buttons' do
386
+ CONTROL_BUTTONS.each do |type, value|
387
+ STATES.each do |state, velocity|
388
+ should "build proper action for control button #{type}, #{state}" do
389
+ stub_input(@device, {:timestamp => 0, :message => [0xB0, value, velocity]})
390
+ assert_equal [{:timestamp => 0, :state => state, :type => type}], @device.read_pending_actions
391
+ end
392
+ end
393
+ end
394
+ end
395
+
396
+ context 'scene buttons' do
397
+ SCENE_BUTTONS.each do |type, value|
398
+ STATES.each do |state, velocity|
399
+ should "build proper action for scene button #{type}, #{state}" do
400
+ stub_input(@device, {:timestamp => 0, :message => [0x90, value, velocity]})
401
+ assert_equal [{:timestamp => 0, :state => state, :type => type}], @device.read_pending_actions
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ context 'grid buttons' do
408
+ 8.times do |x|
409
+ 8.times do |y|
410
+ STATES.each do |state, velocity|
411
+ should "build proper action for grid button #{x},#{y}, #{state}" do
412
+ stub_input(@device, {:timestamp => 0, :message => [0x90, 16 * y + x, velocity]})
413
+ assert_equal [{:timestamp => 0, :state => state, :type => :grid, :x => x, :y => y}], @device.read_pending_actions
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end
419
+
420
+ should 'build proper actions for multiple pending actions' do
421
+ stub_input(@device, {:timestamp => 1, :message => [0x90, 0, 127]}, {:timestamp => 2, :message => [0xB0, 0x68, 0]})
422
+ assert_equal [{:timestamp => 1, :state => :down, :type => :grid, :x => 0, :y => 0}, {:timestamp => 2, :state => :up, :type => :up}], @device.read_pending_actions
423
+ end
424
+
425
+ end
426
+
427
+ end
428
+
429
+ end
@@ -0,0 +1,183 @@
1
+ require 'helper'
2
+
3
+ class BreakError < StandardError; end
4
+
5
+ class TestInteraction < Test::Unit::TestCase
6
+
7
+ context 'initializer' do
8
+
9
+ should 'create device if not given' do
10
+ Launchpad::Device.expects(:new).with(:input => true, :output => true).returns('device')
11
+ assert_equal 'device', Launchpad::Interaction.new.device
12
+ end
13
+
14
+ should 'create device with given device_name' do
15
+ Launchpad::Device.expects(:new).with(:device_name => 'device', :input => true, :output => true).returns('device')
16
+ assert_equal 'device', Launchpad::Interaction.new(:device_name => 'device').device
17
+ end
18
+
19
+ should 'initialize device if given' do
20
+ assert_equal 'device', Launchpad::Interaction.new(:device => 'device').device
21
+ end
22
+
23
+ should 'not be active' do
24
+ assert !Launchpad::Interaction.new.active
25
+ end
26
+
27
+ end
28
+
29
+ context 'start' do
30
+
31
+ # this is kinda greybox tested, since I couldn't come up with another way to test a loop [thomas, 2009-11-11]
32
+
33
+ setup do
34
+ @interaction = Launchpad::Interaction.new(:device => @device = Launchpad::Device.new)
35
+ end
36
+
37
+ context 'up until read_pending_actions' do
38
+
39
+ setup do
40
+ @device.stubs(:read_pending_actions).raises(BreakError)
41
+ end
42
+
43
+ should 'set active to true' do
44
+ begin
45
+ @interaction.start
46
+ fail 'should raise BreakError'
47
+ rescue BreakError
48
+ assert @interaction.active
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ should 'raise CommunicationError when Portmidi::DeviceError occurs' do
55
+ @device.stubs(:read_pending_actions).raises(Portmidi::DeviceError.new(0))
56
+ assert_raise Launchpad::CommunicationError do
57
+ @interaction.start
58
+ end
59
+ end
60
+
61
+ should 'call respond_to_action with actions from respond_to_action' do
62
+ begin
63
+ @interaction.stubs(:sleep).raises(BreakError)
64
+ @device.stubs(:read_pending_actions).returns(['message1', 'message2'])
65
+ @interaction.expects(:respond_to_action).with('message1').once
66
+ @interaction.expects(:respond_to_action).with('message2').once
67
+ @interaction.start
68
+ fail 'should raise BreakError'
69
+ rescue BreakError
70
+ end
71
+ end
72
+
73
+ context 'sleep' do
74
+
75
+ setup do
76
+ @device.stubs(:read_pending_actions).returns([])
77
+ end
78
+
79
+ should 'sleep with default latency of 0.001 when none given' do
80
+ begin
81
+ @interaction.expects(:sleep).with(0.001).raises(BreakError)
82
+ @interaction.start
83
+ fail 'should raise BreakError'
84
+ rescue BreakError
85
+ end
86
+ end
87
+
88
+ should 'sleep with given latency' do
89
+ begin
90
+ @interaction = Launchpad::Interaction.new(:latency => 4, :device => @device)
91
+ @interaction.expects(:sleep).with(4).raises(BreakError)
92
+ @interaction.start
93
+ fail 'should raise BreakError'
94
+ rescue BreakError
95
+ end
96
+ end
97
+
98
+ should 'sleep with absolute value of given negative latency' do
99
+ begin
100
+ @interaction = Launchpad::Interaction.new(:latency => -3.1, :device => @device)
101
+ @interaction.expects(:sleep).with(3.1).raises(BreakError)
102
+ @interaction.start
103
+ fail 'should raise BreakError'
104
+ rescue BreakError
105
+ end
106
+ end
107
+
108
+ should 'not sleep when latency is <= 0' # TODO don't know how to test this [thomas, 2009-11-11]
109
+
110
+ end
111
+
112
+ should 'reset the device after the loop' # TODO don't know how to test this [thomas, 2009-11-11]
113
+
114
+ end
115
+
116
+ context 'stop' do
117
+
118
+ should 'set active to false' do
119
+ i = Launchpad::Interaction.new
120
+ i.instance_variable_set('@active', true)
121
+ assert i.active
122
+ i.stop
123
+ assert !i.active
124
+ end
125
+
126
+ end
127
+
128
+ context 'response_to/no_response_to/respond_to' do
129
+
130
+ setup do
131
+ @interaction = Launchpad::Interaction.new
132
+ end
133
+
134
+ should 'call responses that match, and not others' do
135
+ @interaction.response_to(:mixer, :down) {|i, a| @mixer_down = true}
136
+ @interaction.response_to(:all, :down) {|i, a| @all_down = true}
137
+ @interaction.response_to(:all, :up) {|i, a| @all_up = true}
138
+ @interaction.response_to(:grid, :down) {|i, a| @grid_down = true}
139
+ @interaction.respond_to(:mixer, :down)
140
+ assert @mixer_down
141
+ assert @all_down
142
+ assert !@all_up
143
+ assert !@grid_down
144
+ end
145
+
146
+ should 'not call responses when they are deregistered' do
147
+ @interaction.response_to(:mixer, :down) {|i, a| @mixer_down = true}
148
+ @interaction.response_to(:mixer, :up) {|i, a| @mixer_up = true}
149
+ @interaction.response_to(:all, :both) {|i, a| @all_down = a[:state] == :down}
150
+ @interaction.no_response_to(:mixer, :down)
151
+ @interaction.respond_to(:mixer, :down)
152
+ assert !@mixer_down
153
+ assert !@mixer_up
154
+ assert @all_down
155
+ @interaction.respond_to(:mixer, :up)
156
+ assert !@mixer_down
157
+ assert @mixer_up
158
+ assert !@all_down
159
+ end
160
+
161
+ should 'not call responses registered for both when removing for one of both states' do
162
+ @interaction.response_to(:mixer, :both) {|i, a| @mixer = true}
163
+ @interaction.no_response_to(:mixer, :down)
164
+ @interaction.respond_to(:mixer, :down)
165
+ assert !@mixer
166
+ @interaction.respond_to(:mixer, :up)
167
+ assert @mixer
168
+ end
169
+
170
+ should 'remove other responses when adding a new exclusive response' do
171
+ @interaction.response_to(:mixer, :both) {|i, a| @mixer = true}
172
+ @interaction.response_to(:mixer, :down, :exclusive => true) {|i, a| @exclusive_mixer = true}
173
+ @interaction.respond_to(:mixer, :down)
174
+ assert !@mixer
175
+ assert @exclusive_mixer
176
+ @interaction.respond_to(:mixer, :up)
177
+ assert @mixer
178
+ assert @exclusive_mixer
179
+ end
180
+
181
+ end
182
+
183
+ end