sp-squealer 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,254 @@
1
+ require 'spec_helper'
2
+
3
+ describe Squealer::ProgressBar do
4
+ let(:clock) { Time.new }
5
+ let(:total) { 200 }
6
+ let(:progress_bar) do
7
+ testable_progress_bar = Class.new(Squealer::ProgressBar) do
8
+ attr_reader :emitter
9
+ attr_accessor :clock
10
+ public :total, :ticks, :percentage, :progress_markers, :emit,
11
+ :duration, :start_time, :end_time, :progress_bar_width
12
+
13
+ def console
14
+ @console ||= StringIO.new
15
+ end
16
+
17
+ alias real_start_emitter start_emitter
18
+ def start_emitter; end
19
+ public :real_start_emitter
20
+
21
+ end
22
+ bar = testable_progress_bar.new(total)
23
+ bar.clock = clock
24
+ bar.start
25
+ end
26
+ let(:console) { progress_bar.console }
27
+ let(:progress_bar_width) { progress_bar.progress_bar_width }
28
+
29
+ before { progress_bar.start }
30
+ after { progress_bar.finish }
31
+
32
+ it "allows only one progress bar at a time" do
33
+ Squealer::ProgressBar.new(0).should be_nil
34
+ end
35
+
36
+ it "records the starting time" do
37
+ progress_bar.start_time.should be_an_instance_of(Time)
38
+ end
39
+
40
+ it "records the starting time" do
41
+ progress_bar.start_time.should be_an_instance_of(Time)
42
+ end
43
+
44
+ context "threaded" do
45
+ before { progress_bar.stub(:emitter).and_return(progress_bar.real_start_emitter) }
46
+ after { progress_bar.emitter.kill }
47
+
48
+ it "has an emitter" do
49
+ progress_bar.tick
50
+ progress_bar.emitter.should_not be_nil
51
+ end
52
+
53
+ it "emits at least once" do
54
+ progress_bar.tick
55
+ progress_bar.emitter.wakeup
56
+ sleep 0.1
57
+ console.string.split("\r").length.should > 0
58
+ end
59
+ end
60
+
61
+ context "no items completed" do
62
+ it "emits the total number provided" do
63
+ progress_bar.total.should == total
64
+ end
65
+
66
+ it "emits the number of ticks" do
67
+ progress_bar.ticks.should == 0
68
+ end
69
+
70
+ it "displays an empty bar" do
71
+ progress_bar.progress_markers.size.should == 0
72
+ end
73
+
74
+ it "prints a progress bar to the console" do
75
+ progress_bar.emit
76
+ console.string.should == "\r[#{' ' * progress_bar_width}] #{0}/#{total} (0%) [0/s]"
77
+ end
78
+
79
+ context "0 ticks per second" do
80
+ it "shows 0 documents per second" do
81
+ progress_bar.clock = progress_bar.clock + 60
82
+ progress_bar.emit
83
+ console.string.should =~ %r{ \[0/s\]$}
84
+
85
+ progress_bar.clock = progress_bar.clock + 60
86
+ progress_bar.emit
87
+ console.string.should =~ %r{ \[0/s\]$}
88
+ end
89
+ end
90
+
91
+ context "1 ticks per second" do
92
+ it "shows 1 document per second" do
93
+ progress_bar.clock = progress_bar.clock + 60
94
+ progress_bar.tick
95
+ progress_bar.emit
96
+ console.string.should =~ %r{ \[1/s\]$}
97
+
98
+ progress_bar.clock = progress_bar.clock + 60
99
+ progress_bar.tick
100
+ progress_bar.emit
101
+ console.string.should =~ %r{ \[1/s\]$}
102
+ end
103
+ end
104
+
105
+ context "2 ticks per second" do
106
+ it "shows 2 documents per second" do
107
+ progress_bar.clock = progress_bar.clock + 60
108
+ progress_bar.tick
109
+ progress_bar.tick
110
+ progress_bar.emit
111
+ console.string.should =~ %r{ \[2/s\]$}
112
+
113
+ progress_bar.clock = progress_bar.clock + 60
114
+ progress_bar.tick
115
+ progress_bar.tick
116
+ progress_bar.emit
117
+ console.string.should =~ %r{ \[2/s\]$}
118
+ end
119
+ end
120
+
121
+ context "averaging 2 ticks per second" do
122
+ it "shows 2 documents per second" do
123
+ progress_bar.clock = progress_bar.clock + 60
124
+ progress_bar.tick
125
+ progress_bar.emit
126
+
127
+ progress_bar.clock = progress_bar.clock + 60
128
+ progress_bar.tick
129
+ progress_bar.emit
130
+
131
+ progress_bar.clock = progress_bar.clock + 60
132
+ 4.times { progress_bar.tick }
133
+ progress_bar.emit
134
+ console.string.should =~ %r{ \[2/s\]$}
135
+ end
136
+ end
137
+ end
138
+
139
+ context "one item completed" do
140
+ before { progress_bar.tick }
141
+ it "emits the number of ticks" do
142
+ progress_bar.ticks.should == 1
143
+ end
144
+
145
+ it "emits the number of ticks as a percentage of the total number (rounded down)" do
146
+ progress_bar.percentage.should == 0
147
+ end
148
+ end
149
+
150
+ context "1/nth complete (where n is the width of the progress bar)" do
151
+ let(:ticks) { (total / progress_bar_width) }
152
+ before { ticks.times { progress_bar.tick } }
153
+ it "emits the number of ticks" do
154
+ progress_bar.ticks.should == ticks
155
+ end
156
+
157
+ it "emits the number of ticks as a percentage of the total number (rounded down)" do
158
+ progress_bar.percentage.should == (ticks.to_f * 100 / total).floor
159
+ end
160
+
161
+ it "displays the first progress marker" do
162
+ progress_bar.progress_markers.size.should == 1
163
+ end
164
+
165
+ it "prints a progress bar to the console" do
166
+ progress_bar.emit
167
+ percentage = (ticks.to_f * 100 / total).floor
168
+ console.string.should == "\r[=#{' ' * (progress_bar_width - 1)}] #{ticks}/#{total} (#{percentage}%) [0/s]"
169
+ end
170
+ end
171
+
172
+ context "all but one item completed" do
173
+ let(:ticks) { total - 1 }
174
+ before { ticks.times { progress_bar.tick } }
175
+
176
+ it "emits the number of ticks" do
177
+ progress_bar.ticks.should == ticks
178
+ end
179
+
180
+ it "emits the number of ticks as a percentage of the total number (rounded down)" do
181
+ progress_bar.percentage.should == 99
182
+ end
183
+
184
+ it "has not yet displayed the final progress marker" do
185
+ progress_bar.progress_markers.size.should == (progress_bar_width - 1)
186
+ end
187
+ end
188
+
189
+ context "all items completed" do
190
+ let(:ticks) { total }
191
+ before { ticks.times { progress_bar.tick } }
192
+
193
+ it "emits the number of ticks" do
194
+ progress_bar.ticks.should == ticks
195
+ end
196
+
197
+ it "emits 100%" do
198
+ progress_bar.percentage.should == 100
199
+ end
200
+
201
+ it "fills the progress bar with progress markers" do
202
+ progress_bar.progress_markers.size.should == progress_bar_width
203
+ end
204
+
205
+ it "records the ending time when finished" do
206
+ progress_bar.finish
207
+ progress_bar.end_time.should be_an_instance_of(Time)
208
+ end
209
+
210
+ it "prints a progress bar to the console" do
211
+ progress_bar.finish
212
+ progress_bar.emit
213
+ console.string.split("\n").first.should == "\r[#{'=' * progress_bar_width}] #{ticks}/#{total} (100%) [0/s]"
214
+ end
215
+ end
216
+
217
+ context "multiple emits" do
218
+ let(:ticks) { total }
219
+ subject { console.string }
220
+ before do
221
+ progress_bar.emit
222
+ end
223
+
224
+ context "not done" do
225
+ it "emitted two lines with no final newline" do
226
+ progress_bar.emit
227
+
228
+ subject.split("\r").size.should == 3
229
+ subject[-1, 1].should_not == "\n"
230
+ end
231
+ end
232
+
233
+ context "done" do
234
+ it "emitted two lines with a final newline" do
235
+ ticks.times { progress_bar.tick }
236
+ progress_bar.finish
237
+ progress_bar.emit
238
+
239
+ subject.split("\r").size.should == 3
240
+ subject[-1, 1].should == "\n"
241
+ end
242
+
243
+ it "emitted final timings" do
244
+ ticks.times { progress_bar.tick }
245
+ progress_bar.finish
246
+ progress_bar.emit
247
+
248
+ subject.should include("Start: #{progress_bar.start_time}\n")
249
+ subject.should include("End: #{progress_bar.end_time}\n")
250
+ subject.should include("Duration: #{progress_bar.duration}\n")
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,288 @@
1
+ require 'spec_helper'
2
+
3
+ describe Squealer::Target do
4
+ let(:table_name) { :test_table }
5
+ let(:test_table) { {'_id' => 0} }
6
+
7
+ let(:upsertable?) { Squealer::Database.instance.upsertable? }
8
+
9
+ after(:each) { Squealer::Target::Queue.instance.clear }
10
+
11
+ context "targeting" do
12
+ describe "initialize" do
13
+ let(:faqs) { [{'_id' => '123'}] }
14
+ let(:naqs) { [{:_id => 'abc'}] }
15
+
16
+ context "without a target row id" do
17
+ context "with the inferred variable in scope" do
18
+ it "infers the value from the _id field in the hashmap referenced by the variable" do
19
+ faqs.each do |faq|
20
+ Squealer::TestTarget.new(nil, :faq) do |target|
21
+ target.instance_variable_get('@row_id').should == faq['_id']
22
+ end
23
+ end
24
+ end
25
+ it "infers the value from the :_id field in the hashmap referenced by the variable" do
26
+ naqs.each do |naq|
27
+ Squealer::TestTarget.new(nil, :naq) do |target|
28
+ target.instance_variable_get('@row_id').should == naq[:_id]
29
+ end
30
+ end
31
+ end
32
+
33
+ context "but it doesn't have an _id key" do
34
+ it "throws an argument error" do
35
+ hash_with_no_id = {}
36
+ lambda do
37
+ Squealer::TestTarget.new(nil, :hash_with_no_id) {}
38
+ end.should raise_error(ArgumentError)
39
+ end
40
+ end
41
+
42
+ context "but it isn't a hashmap" do
43
+ it "throws an argument error" do
44
+ not_a_hash = nil
45
+ lambda do
46
+ Squealer::TestTarget.new(nil, :not_a_hash) {}
47
+ end.should raise_error(ArgumentError)
48
+ end
49
+ end
50
+ end
51
+
52
+ context "without the inferred variable in scope" do
53
+ it "throws a name error" do
54
+ lambda do
55
+ Squealer::TestTarget.new(nil, :missing_variable) {}
56
+ end.should raise_error(NameError)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ context "nesting" do
65
+ it "pushes itself onto the targets stack when starting" do
66
+ @target1 = @target2 = nil
67
+
68
+ target1 = Squealer::TestTarget.new(nil, table_name) do
69
+ @target1 = Squealer::Target.current
70
+ test_table_2 = test_table
71
+ Squealer::TestTarget.new(nil, "#{table_name}_2") do
72
+ @target2 = Squealer::Target.current
73
+ @target2.should_not == @target1
74
+ end
75
+ end
76
+ target1.should === @target1
77
+ end
78
+
79
+ it "pops itself off the targets stack when finished" do
80
+ Squealer::TestTarget.new(nil, table_name) { nil }
81
+ Squealer::Target.current.should be_nil
82
+ end
83
+ end
84
+
85
+
86
+ context "yielding" do
87
+ it "yields" do
88
+ block_done = false
89
+ target = Squealer::TestTarget.new(nil, table_name) { block_done = true }
90
+ block_done.should be_true
91
+ end
92
+
93
+ it "yields inner blocks before executing its own SQL" do
94
+ blocks_done = []
95
+ Squealer::TestTarget.new(nil, table_name) do |target_1|
96
+ blocks_done << target_1
97
+ blocks_done.first.sql.should be_empty
98
+ Squealer::TestTarget.new(nil, table_name) do |target_2|
99
+ blocks_done << target_2
100
+ blocks_done.first.sql.should be_empty
101
+ blocks_done.last.sql.should be_empty
102
+ end
103
+ blocks_done.first.sql.should be_empty
104
+ blocks_done.last.sql.should_not be_empty
105
+ end
106
+ blocks_done.first.sql.should_not be_empty
107
+ blocks_done.last.sql.should_not be_empty
108
+ end
109
+ end
110
+
111
+
112
+ context "assigning" do
113
+ describe "#assign" do
114
+ let(:col1) { :meaning }
115
+ let(:value1) { 42 }
116
+ let(:faqs) { [{'_id' => nil, col1.to_s => value1}] }
117
+ let(:askers) { [{'_id' => 2001, 'name' => 'Zarathustra'}] }
118
+
119
+ context "with a block" do
120
+ it "uses the value from the block" do
121
+ faqs.each do |faq|
122
+ Squealer::TestTarget.new(nil, :faq) do
123
+ assign(col1) { value1 }
124
+ Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
125
+ Squealer::Target.current.instance_variable_get('@column_values').should == [value1]
126
+ end
127
+ end
128
+ end
129
+ it "uses the value from the block even if it is nil" do
130
+ faqs.each do |faq|
131
+ Squealer::TestTarget.new(nil, :faq) do
132
+ assign(col1) { nil }
133
+ Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
134
+ Squealer::Target.current.instance_variable_get('@column_values').should == [nil]
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context "without a block" do
141
+ context "with the inferred variable in scope" do
142
+ it "infers source from target name" do
143
+ faqs.each do |faq|
144
+ Squealer::TestTarget.new(nil, :faq) do
145
+ assign(col1)
146
+ Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
147
+ Squealer::Target.current.instance_variable_get('@column_values').should == [value1]
148
+ end
149
+ end
150
+ end
151
+ it "infers related source from target name" do
152
+ askers.each do |asker|
153
+ faqs.each do |faq|
154
+ Squealer::TestTarget.new(nil, :faq) do
155
+ assign(:asker_id)
156
+ Squealer::Target.current.instance_variable_get('@column_names').should == [:asker_id]
157
+ Squealer::Target.current.instance_variable_get('@column_values').should == [2001]
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ context "with an empty block" do
166
+ it "assumes nil" do
167
+ faqs.each do |faq|
168
+ Squealer::TestTarget.new(nil, :faq) do
169
+ assign(col1) {}
170
+ Squealer::Target.current.instance_variable_get('@column_names').should == [col1]
171
+ Squealer::Target.current.instance_variable_get('@column_values').should == [nil]
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+
180
+ context "exporting" do
181
+ it "sends the sql to the export database" do
182
+ Squealer::TestTarget.new(nil, table_name) { nil }
183
+ end
184
+
185
+ describe "#target" do
186
+ describe "#typecast_values" do
187
+ subject { target.send(:typecast_values) }
188
+ let(:target) { Squealer::TestTarget.new(nil, table_name) {} }
189
+
190
+ it "casts array to comma-separated string" do
191
+ target.assign(:colA) { ['1', '2'] }
192
+ subject.should == ['1,2']
193
+ end
194
+ end
195
+
196
+ context "generates SQL command strings" do
197
+ let(:target) { Squealer::TestTarget.new(nil, table_name) { nil } }
198
+
199
+ context "insert" do
200
+ it "targets the table" do
201
+ target.sql.should =~ /^INSERT INTO "#{table_name}" / if upsertable?
202
+ target.sql.should =~ /; INSERT INTO "#{table_name}" / unless upsertable?
203
+ end
204
+
205
+ it "includes the primary key name in the INSERT" do
206
+ target.sql.should =~ / \(id\) VALUES/
207
+ end
208
+
209
+ it "includes the primary key value in the INSERT" do
210
+ target.sql.should =~ / VALUES \('0'\)/
211
+ end
212
+ end
213
+
214
+ context "upsert" do
215
+ before { Squealer::Database.instance.should_receive(:upsertable?).at_least(:once).and_return(true) }
216
+ it "uses an INSERT ... ON DUPLICATE KEY UPDATE statement" do
217
+ target.sql.should =~ /^INSERT INTO .* ON DUPLICATE KEY UPDATE /
218
+ end
219
+ end
220
+ end
221
+
222
+ context "with 2 columns" do
223
+ let(:value_1) { 42 }
224
+ let(:target) do
225
+ Squealer::TestTarget.new(nil, table_name) { Squealer::Target.current.assign(:colA) { value_1 } }
226
+ end
227
+
228
+ context "insert" do
229
+ it "includes the column name in the INSERT" do
230
+ target.sql.should =~ /\(id,"colA"\) VALUES/
231
+ end
232
+
233
+ it "includes the column value in the INSERT" do
234
+ target.sql.should =~ /VALUES \('0',\?\)/
235
+ end
236
+ end
237
+
238
+ context "upsert" do
239
+ before { Squealer::Database.instance.should_receive(:upsertable?).at_least(:once).and_return(true) }
240
+ it "includes the column name and value in the UPDATE" do
241
+ target.sql.should =~ /UPDATE "colA"=\?/
242
+ end
243
+ end
244
+ end
245
+
246
+ context "with 3 columns" do
247
+ let(:value_1) { 42 }
248
+ let(:value_2) { 'foobar' }
249
+ let(:target) do
250
+ Squealer::TestTarget.new(nil, table_name) do |target|
251
+ Squealer::Target.current.assign(:colA) { value_1 }
252
+ Squealer::Target.current.assign(:colB) { value_2 }
253
+ end
254
+ end
255
+
256
+ context "insert" do
257
+ it "includes the column names in the INSERT" do
258
+ target.sql.should =~ /\(id,"colA","colB"\) VALUES/
259
+ end
260
+
261
+ it "includes the column values in the INSERT" do
262
+ target.sql.should =~ /VALUES \('0',\?,\?\)/
263
+ end
264
+ end
265
+
266
+ context "upsert" do
267
+ before { Squealer::Database.instance.should_receive(:upsertable?).at_least(:once).and_return(true) }
268
+ it "includes the column names and values in the UPDATE" do
269
+ target.sql.should =~ /UPDATE "colA"=\?,"colB"=\?/
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ # Yes, David Chelimsky, an any_instance stub would be handy right about now :-P
278
+ module Squealer
279
+ class TestTarget < Target
280
+ def execute_sql(sql, values)
281
+ end
282
+ end
283
+ end
284
+
285
+ module BSON
286
+ class ObjectID; end
287
+ end
288
+