dispatch 0.0.1pre

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