dispatch 0.0.1pre

Sign up to get free protection for your applications and to get access to all the features.
data/spec/job_spec.rb ADDED
@@ -0,0 +1,103 @@
1
+ require "spec_helper"
2
+
3
+ if MACOSX_VERSION >= 10.6
4
+ describe "Dispatch::Job" do
5
+
6
+ before :each do
7
+ @result = 0
8
+ @job = Dispatch::Job.new
9
+ end
10
+
11
+ describe :new do
12
+ it "should return a Job for tracking execution of passed blocks" do
13
+ @job.should be_kind_of Dispatch::Job
14
+ end
15
+
16
+ it "should use default queue" do
17
+ @job.add { Dispatch::Queue.current }
18
+ @job.value.to_s.should == Dispatch::Queue.concurrent.to_s
19
+ end
20
+
21
+ it "should take an optional queue" do
22
+ q = Dispatch::Queue.concurrent(:high)
23
+ job = Dispatch::Job.new(q) { Dispatch::Queue.current }
24
+ job.value.to_s.should == q.to_s
25
+ end
26
+ end
27
+
28
+ describe :group do
29
+ it "should return an instance of Dispatch::Group" do
30
+ @job.group.should be_kind_of Dispatch::Group
31
+ end
32
+ end
33
+
34
+ describe :values do
35
+ it "should return an instance of Dispatch::Proxy" do
36
+ @job.values.should be_kind_of Dispatch::Proxy
37
+ end
38
+
39
+ it "has a __value__ that is Enumerable" do
40
+ @job.values.__value__.should be_kind_of Enumerable
41
+ end
42
+ end
43
+
44
+ describe :add do
45
+ it "should schedule a block for async execution" do
46
+ @value = 0
47
+ @job.add { sleep 0.01; @value = 42 }
48
+ @value.should == 0
49
+ @job.join
50
+ @value.should == 42
51
+ end
52
+ end
53
+
54
+ describe :join do
55
+ it "should wait when called Synchronously" do
56
+ @value = 0
57
+ @job.add { sleep 0.01; @value = 42 }
58
+ @job.join
59
+ @value.should == 42
60
+ end
61
+
62
+ it "should invoke passed block Asynchronously" do
63
+ @value = 0
64
+ @rval = 0
65
+ @job.add { sleep 0.01; @value = 42 }
66
+ q = Dispatch::Queue.for(@value)
67
+ @job.join(q) { sleep 0.01; @rval = @value }
68
+ @job.join
69
+ @rval.should == 0
70
+ q.sync { }
71
+ @rval.should == 42
72
+ end
73
+ end
74
+
75
+ describe :value do
76
+ it "should return value when called Synchronously" do
77
+ @job.add { Math.sqrt(2**10) }
78
+ @job.value.should == 2**5
79
+ end
80
+
81
+ it "should invoke passed block Asynchronously with return value" do
82
+ @job.add { Math.sqrt(2**10) }
83
+ @value = 0
84
+ q = Dispatch::Queue.for(@value)
85
+ @job.value(q) {|v| @value = v}
86
+ @job.join
87
+ q.sync { }
88
+ @value.should == 2**5
89
+ end
90
+ end
91
+
92
+ describe :synchronize do
93
+ it "should return serialization Proxy for passed object" do
94
+ actee = {}
95
+ actor = @job.sync(actee)
96
+ actor.should be_kind_of Dispatch::Proxy
97
+ actor.__value__.should == actee
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,105 @@
1
+ require "spec_helper"
2
+
3
+ if MACOSX_VERSION >= 10.6
4
+
5
+ class Delegate
6
+ def initialize(s); @s = s; end
7
+ def current_queue; Dispatch::Queue.current; end
8
+ def takes_block(&block); block.call; end
9
+ def get_number(); sleep 0.01; 42; end
10
+ def set_name(s); sleep 0.01; @s = s; end
11
+ def to_s; @s.to_s; end
12
+ end
13
+
14
+ describe "Dispatch::Proxy" do
15
+ before :each do
16
+ @delegate_name = "my_delegate"
17
+ @delegate = Delegate.new(@delegate_name)
18
+ @proxy = Dispatch::Proxy.new(@delegate)
19
+ end
20
+
21
+ describe :new do
22
+ it "returns a Dispatch::Proxy" do
23
+ @proxy.should be_kind_of Dispatch::Proxy
24
+ @proxy.should be_kind_of SimpleDelegator
25
+ end
26
+
27
+ it "takes an optional group and queue for callbacks" do
28
+ g = Dispatch::Group.new
29
+ q = Dispatch::Queue.concurrent(:high)
30
+ proxy = Dispatch::Proxy.new(@delegate, g, q)
31
+ proxy.__group__.should == g
32
+ proxy.__queue__.should == q
33
+ end
34
+
35
+ it "creates a default Group if none specified" do
36
+ @proxy.__group__.should be_kind_of Dispatch::Group
37
+ end
38
+
39
+ it "uses default queue if none specified" do
40
+ @proxy.__queue__.should == Dispatch::Queue.concurrent
41
+ end
42
+ end
43
+
44
+ describe :delegate do
45
+ it "should be returned by __getobj__" do
46
+ @proxy.__getobj__.should == @delegate
47
+ end
48
+
49
+ it "should be invoked for methods it defines" do
50
+ @proxy.to_s.should == @delegate_name
51
+ end
52
+ end
53
+
54
+ describe :method_missing do
55
+ it "runs methods on a private serial queue" do
56
+ q = @proxy.current_queue
57
+ q.label.should =~ /proxy/
58
+ end
59
+
60
+ it "should return Synchronously if block NOT given" do
61
+ retval = @proxy.get_number
62
+ retval.should == 42
63
+ end
64
+
65
+ it "should otherwise return Asynchronous to block, if given" do
66
+ @value = 0
67
+ retval = @proxy.get_number { |v| @value = v }
68
+ @value.should == 0
69
+ retval.should == nil
70
+ @proxy.__group__.wait
71
+ @value.should == 42
72
+ end
73
+ end
74
+
75
+ describe :__value__ do
76
+ it "should complete work and return delegate" do
77
+ new_name = "nobody"
78
+ @proxy.set_name(new_name) { }
79
+ d = @proxy.__value__
80
+ d.should be_kind_of Delegate
81
+ d.to_s.should == new_name
82
+ end
83
+ end
84
+
85
+ describe "state" do
86
+ it "should persist for collection objects" do
87
+ actor = Dispatch::Proxy.new([])
88
+ actor.size.should == 0
89
+ actor << :foo
90
+ actor.size.should == 1
91
+ actor[42] = :foo
92
+ actor.size.should == 43
93
+ actor.should be_kind_of Dispatch::Proxy
94
+ end
95
+
96
+ it "should NOT persist under assignment" do
97
+ actor = Dispatch::Proxy.new(0)
98
+ actor.should be_kind_of Dispatch::Proxy
99
+ actor += 1
100
+ actor.should_not be_kind_of Dispatch::Proxy
101
+ end
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ if MACOSX_VERSION >= 10.6
4
+
5
+ describe "Dispatch::Queue" do
6
+ before :each do
7
+ @my_object = "Hello, World!"
8
+ @q = Dispatch::Queue.for(@my_object)
9
+ end
10
+
11
+ describe :labelize do
12
+ it "should return a unique label for any object" do
13
+ s1 = Dispatch::Queue.labelize @my_object
14
+ s2 = Dispatch::Queue.labelize @my_object
15
+ s1.should_not == s2
16
+ end
17
+ end
18
+
19
+ describe :for do
20
+ it "should return a dispatch queue" do
21
+ @q.should be_kind_of Dispatch::Queue
22
+ end
23
+
24
+ it "should return a unique queue for each object" do
25
+ q = Dispatch::Queue.for(@my_object)
26
+ @q.should_not == q
27
+ end
28
+
29
+ it "should return a unique label for each queue" do
30
+ q = Dispatch::Queue.for(@my_object)
31
+ @q.to_s.should_not == q.to_s
32
+ end
33
+ end
34
+
35
+ describe :join do
36
+ it "should wait until pending blocks execute " do
37
+ @n = 0
38
+ @q.async {@n = 42}
39
+ @n.should == 0
40
+ @q.join
41
+ @n.should == 42
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,190 @@
1
+ require "spec_helper"
2
+
3
+ if MACOSX_VERSION >= 10.6
4
+
5
+ describe "Dispatch::Source" do
6
+ before :each do
7
+ @q = Dispatch::Queue.new('org.macruby.gcd_spec.prelude')
8
+ @src = nil
9
+ end
10
+
11
+ after :each do
12
+ @src.cancel! if not @src.nil? and not @src.cancelled?
13
+ @q.sync { }
14
+ end
15
+
16
+
17
+ describe :event2num do
18
+ it "converts PROC symbol to int" do
19
+ Dispatch::Source.event2num(:signal).should == Dispatch::Source::PROC_SIGNAL
20
+ end
21
+
22
+ it "converts VNODE symbol to int" do
23
+ Dispatch::Source.event2num(:rename).should == Dispatch::Source::VNODE_RENAME
24
+ end
25
+ end
26
+
27
+ describe :data2events do
28
+ it "converts PROC bitfields to symbols" do
29
+ mask = Dispatch::Source::PROC_EXIT | Dispatch::Source::PROC_SIGNAL
30
+ events = Dispatch::Source.data2events(mask)
31
+ events.include?(:signal).should == true
32
+ events.include?(:fork).should == false
33
+ end
34
+
35
+ it "converts VNODE bitfields to symbols" do
36
+ mask = Dispatch::Source::VNODE_DELETE | Dispatch::Source::VNODE_WRITE
37
+ events = Dispatch::Source.data2events(mask)
38
+ events.include?(:delete).should == true
39
+ events.include?(:rename).should == false
40
+ end
41
+ end
42
+
43
+ describe "add" do
44
+ it "fires with data on summed inputs" do
45
+ @count = 0
46
+ @src = Dispatch::Source.add(@q) {|s| @count += s.data}
47
+ @src << 20
48
+ @src << 22
49
+ @q.sync {}
50
+ @count.should == 42
51
+ end
52
+ end
53
+
54
+ describe "or" do
55
+ it "fires with data on ORed inputs" do
56
+ @count = 0
57
+ @src = Dispatch::Source.or(@q) {|s| @count += s.data}
58
+ @src << 0b101_000
59
+ @src << 0b000_010
60
+ @q.sync {}
61
+ @count.should == 42
62
+ end
63
+ end
64
+
65
+ describe "PROC" do
66
+ before :each do
67
+ @signal = Signal.list["USR1"]
68
+ end
69
+
70
+ describe "process" do
71
+
72
+ it "fires with data on process event(s)" do
73
+ @event = 0
74
+ @events = []
75
+ @src = Dispatch::Source.process($$, %w(exit fork exec signal), @q) do |s|
76
+ @event += s.data
77
+ @events += Dispatch::Source.data2events(s.data)
78
+ end
79
+ Signal.trap(@signal, "IGNORE")
80
+ Process.kill(@signal, $$)
81
+ Signal.trap(@signal, "DEFAULT")
82
+ @q.sync {}
83
+ @events.include?(:signal).should == true
84
+ end
85
+
86
+ it "can use bitfields as well as arrays" do
87
+ mask = Dispatch::Source::PROC_EXIT | Dispatch::Source::PROC_SIGNAL
88
+ @event = 0
89
+ @src = Dispatch::Source.process($$, mask, @q) { |s| @event |= s.data }
90
+ Signal.trap(@signal, "IGNORE")
91
+ Process.kill(@signal, $$)
92
+ Signal.trap(@signal, "DEFAULT")
93
+ @q.sync {}
94
+ @event.should == Dispatch::Source.event2num(:signal)
95
+ end
96
+ end
97
+
98
+ describe "signal" do
99
+ it "fires with data on signal count" do
100
+ @count = 0
101
+ @src = Dispatch::Source.signal(@signal, @q) {|s| @count += s.data}
102
+ Signal.trap(@signal, "IGNORE")
103
+ Process.kill(@signal, $$)
104
+ Process.kill(@signal, $$)
105
+ Signal.trap(@signal, "DEFAULT")
106
+ @q.sync {}
107
+ @count.should == 2
108
+ @src.cancel!
109
+ end
110
+ end
111
+ end
112
+
113
+ describe "VNODE" do
114
+ before :each do
115
+ @msg = "#{$$}-#{Time.now.to_s.gsub(' ','_')}"
116
+ @filename = tmp("gcd_spec_source-#{@msg}")
117
+ @file = nil
118
+ @src = nil
119
+ end
120
+
121
+ after :each do
122
+ @src.cancel! if not @src.nil? and not @src.cancelled?
123
+ @q.sync { }
124
+ @file.close if not @file.closed?
125
+ File.delete(@filename)
126
+ end
127
+
128
+ describe "read" do
129
+ it "fires with data on readable bytes" do
130
+ File.open(@filename, "w") {|f| f.print @msg}
131
+ @file = File.open(@filename, "r")
132
+ @result = ""
133
+ @src = Dispatch::Source.read(@file, @q) {|s| @result<<@file.read(s.data)}
134
+ while (@result.size < @msg.size) do; end
135
+ @q.sync { }
136
+ @result.should == @msg
137
+ end
138
+ end
139
+
140
+ describe "write" do
141
+ it "fires with data on writable bytes" do
142
+ @file = File.open(@filename, "w")
143
+ @message = @msg
144
+ @src = Dispatch::Source.write(@file, @q) do |s|
145
+ if @message.size > 0 then
146
+ char = @message[0..0]
147
+ @file.write(char)
148
+ @message = @message[1..-1]
149
+ end
150
+ end
151
+ while (@message.size > 0) do; end
152
+ result = File.read(@filename)
153
+ @result.should == @msg
154
+ end
155
+ end
156
+
157
+ describe "file" do
158
+ it "fires with data on file events" do
159
+ write_flag = Dispatch::Source.event2num(:write)
160
+ @file = File.open(@filename, "w")
161
+ @fired = false
162
+ @mask = 0
163
+ events = %w(delete write extend attrib link rename revoke)
164
+ @src = Dispatch::Source.file(@file, events, @q) do |s|
165
+ @mask |= s.data
166
+ @fired = true
167
+ end
168
+ @file.write(@msg)
169
+ @file.flush
170
+ @q.sync { }
171
+ @fired.should == true
172
+ (@mask & write_flag).should == write_flag
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "periodic" do
178
+ it "fires with data on how often the timer has fired" do
179
+ @count = -1
180
+ repeats = 2
181
+ @periodic = 0.02
182
+ @src = Dispatch::Source.periodic(@periodic, @q) {|s| @count += s.data}
183
+ sleep repeats*@periodic
184
+ @q.sync { }
185
+ @count.should == repeats
186
+ end
187
+ end
188
+ end
189
+
190
+ end
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), "../lib/dispatch")
2
+
3
+ framework 'Cocoa'
4
+
5
+ SPEC_ROOT = File.dirname(__FILE__)
6
+ FIXTURES = File.join(SPEC_ROOT, "fixtures")
7
+
8
+ class FixtureCompiler
9
+ def self.require!(fixture)
10
+ new(fixture).require!
11
+ end
12
+
13
+ FRAMEWORKS = %w{ Foundation }
14
+ ARCHS = %w{ i386 x86_64 }
15
+ OPTIONS = %w{ -g -dynamiclib -fobjc-gc -Wl,-undefined,dynamic_lookup }
16
+ GCC = "/usr/bin/gcc"
17
+
18
+ attr_reader :gcc, :frameworks, :archs, :options
19
+ attr_reader :fixture, :bundle, :bridge_support
20
+
21
+ def initialize(fixture)
22
+ @fixture = File.join(FIXTURES, "#{fixture}.m")
23
+ @bundle = File.join("/tmp", "#{fixture}.bundle")
24
+ @bridge_support = File.join(FIXTURES, "#{fixture}.bridgesupport")
25
+
26
+ @gcc, @frameworks, @archs, @options = [GCC, FRAMEWORKS, ARCHS, OPTIONS].map { |x| x.dup }
27
+ end
28
+
29
+ def require!
30
+ compile!
31
+ load!
32
+ end
33
+
34
+ private
35
+
36
+ def needs_update?
37
+ !File.exist?(bundle) or File.mtime(fixture) > File.mtime(bundle)
38
+ end
39
+
40
+ def compile!
41
+ if needs_update?
42
+ puts "[!] Compiling fixture `#{fixture}'"
43
+
44
+ a = archs.map { |a| "-arch #{a}" }.join(' ')
45
+ o = options.join(' ')
46
+ f = frameworks.map { |f| "-framework #{f}" }.join(' ')
47
+
48
+ `#{gcc} #{fixture} -o #{bundle} #{f} #{o} #{a}`
49
+ end
50
+ end
51
+
52
+ def load!
53
+ require bundle[0..-8]
54
+ if File.exist? bridge_support
55
+ load_bridge_support_file bridge_support
56
+ end
57
+ end
58
+ end
59
+
60
+ MACOSX_VERSION = `sw_vers -productVersion`.to_f