spreadsheet_agent 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,411 @@
1
+ require 'test/unit'
2
+ require 'shoulda/context'
3
+ require 'spreadsheet_agent'
4
+ require 'google_drive'
5
+ require 'psych'
6
+ require 'socket'
7
+
8
+ class TC_SpreadsheetAgentTest < Test::Unit::TestCase
9
+
10
+ context 'Agent' do
11
+
12
+ setup do
13
+ @config_file = File.expand_path(File.dirname( __FILE__ )) + '/../config/agent.conf.yml'
14
+
15
+ unless File.exists? @config_file
16
+ $stderr.puts "You must create a valid test Google Spreadsheet and a valid #{ @config_file } configuration file pointing to it to run the tests. See README.txt file for more information on how to run the tests."
17
+ exit(1)
18
+ end
19
+
20
+ @testing_page_name = 'testing'
21
+ @keys = { 'testentry' => 'test', 'testpage' => @testing_page_name }
22
+ @testing_page = nil
23
+ end
24
+
25
+ teardown do
26
+ unless @testing_page.nil?
27
+ @testing_page.max_rows = 1
28
+ colnum = 1
29
+ while colnum <= @testing_page.num_cols
30
+ @testing_page[1,colnum] = nil
31
+ colnum += 1
32
+ end
33
+ @testing_page.save
34
+ end
35
+ end
36
+
37
+ context 'instantiated' do
38
+
39
+ should 'be a SpreadsheetAgent::Agent' do
40
+ google_agent = SpreadsheetAgent::Agent.new(
41
+ :agent_name => 'instantiate',
42
+ :page_name => @testing_page_name,
43
+ :keys => @keys,
44
+ :config_file => @config_file
45
+ )
46
+ assert_not_nil google_agent, 'google_agent is nil!'
47
+ assert_instance_of(SpreadsheetAgent::Agent, google_agent)
48
+
49
+ end
50
+
51
+ end #instantiated
52
+
53
+ context 'that is not ready' do
54
+
55
+ setup do
56
+ @agent_name = 'donotrun'
57
+ @google_agent = prepare_google_agent_for(@agent_name, false)
58
+ end
59
+
60
+ should 'return true from process! but not run the supplied code' do
61
+ subroutine_ran = false
62
+ process_ran = @google_agent.process! do |entry|
63
+ # this should not run at all
64
+ subroutine_ran = true
65
+ true
66
+ end
67
+ assert process_ran, 'process! should return true'
68
+ assert !subroutine_ran, 'The subref should not have run at all'
69
+ entry = @google_agent.get_entry
70
+ assert entry[@agent_name].empty?, "#{ @agent_name } field should still be empty"
71
+ end
72
+
73
+ end #that is not ready
74
+
75
+ context 'that is ready' do
76
+
77
+ setup do
78
+ @agent_name = 'readytorun'
79
+ @google_agent = prepare_google_agent_for(@agent_name, true)
80
+ end
81
+
82
+ should 'return true from process!, run passing code, and complete the agent_name field' do
83
+ subroutine_ran = false
84
+ process_ran = @google_agent.process! do |entry|
85
+ subroutine_ran = true
86
+ true
87
+ end
88
+
89
+ assert process_ran, 'process should return true'
90
+ assert subroutine_ran, 'The subref should have run'
91
+ entry = @google_agent.get_entry
92
+ assert_equal "1", entry[@agent_name], "#{ @agent_name } field should have completed successfully"
93
+ end
94
+
95
+ should 'return true from process!, run failing code, and Fail the agent_name field' do
96
+ subroutine_ran = false
97
+ process_ran = @google_agent.process! do |entry|
98
+ subroutine_ran = true
99
+ false
100
+ end
101
+
102
+ assert process_ran, 'process should return true'
103
+ assert subroutine_ran, 'The subref should have run'
104
+ entry = @google_agent.get_entry
105
+ expected_field_value = ['F', Socket.gethostname].join(':')
106
+ assert_equal expected_field_value, entry[@agent_name], "#{ @agent_name } field should have failed with #{ expected_field_value}, was #{ entry[@agent_name] }"
107
+ end
108
+
109
+ should 'be able to update cell contents on success' do
110
+ updated_cell_name = 'updatevalue'
111
+ updated_cell_value = 'iamupdated';
112
+ add_header_to_page(updated_cell_name, @testing_page)
113
+ subroutine_ran = false
114
+
115
+ process_ran = @google_agent.process! do |entry|
116
+ subroutine_ran = true
117
+ [true, { updated_cell_name => updated_cell_value }]
118
+ end
119
+
120
+ assert process_ran, 'process should return true'
121
+ assert subroutine_ran, 'The subref should have run'
122
+ entry = @google_agent.get_entry
123
+ assert_equal "1", entry[@agent_name], "#{ @agent_name } field should have completed successfully"
124
+ assert_equal updated_cell_value, entry[updated_cell_name]
125
+ end
126
+
127
+ end #that is ready
128
+
129
+ context 'that requires a prerequisite' do
130
+
131
+ setup do
132
+ @agent_name = 'readyifprereq'
133
+ @prerequisite_cell_name = 'prerequisitecell'
134
+ @prerequisites = [ @prerequisite_cell_name ]
135
+ @google_agent = prepare_google_agent_for(@agent_name, true, nil, { :prerequisites => @prerequisites } )
136
+ add_header_to_page(@prerequisite_cell_name, @testing_page)
137
+ end
138
+
139
+ should 'set prerequisites on agent' do
140
+ assert_not_nil @google_agent.prerequisites
141
+ assert_equal @prerequisites.count, @google_agent.prerequisites.count
142
+ @prerequisites.each do |prereq|
143
+ assert @google_agent.prerequisites.include?(prereq), "#{ prereq } should be a prerequisite"
144
+ end
145
+ end
146
+
147
+ should 'not run if prerequisite has not run' do
148
+ entry = @google_agent.get_entry
149
+ assert entry[@prerequisite_cell_name].empty?, "#{ @prerequisite_cell_name } is #{ entry[@prerequisite_cell_name] } but should be empty!"
150
+
151
+ subroutine_ran = false
152
+ process_ran = @google_agent.process! do |entry|
153
+ # this should not run at all
154
+ subroutine_ran = true
155
+ true
156
+ end
157
+
158
+ assert process_ran, 'process! should return true'
159
+ assert !subroutine_ran, 'The subref should not have run at all'
160
+ entry = @google_agent.get_entry
161
+ assert entry[@agent_name].empty?, "#{ @agent_name } field should still be empty"
162
+ end
163
+
164
+ should 'not run if prerequisite has failed' do
165
+ failed_value = ['F', Socket.gethostname].join(':')
166
+ entry = @google_agent.get_entry
167
+ entry.update(@prerequisite_cell_name => failed_value)
168
+ @testing_page.save
169
+
170
+ subroutine_ran = false
171
+ process_ran = @google_agent.process! do |entry|
172
+ # this should not run at all
173
+ subroutine_ran = true
174
+ true
175
+ end
176
+
177
+ assert process_ran, 'process! should return true'
178
+ assert !subroutine_ran, 'The subref should not have run at all'
179
+ entry = @google_agent.get_entry
180
+ assert_equal failed_value, entry[@prerequisite_cell_name]
181
+ assert entry[@agent_name].empty?, "#{ @agent_name } field should still be empty"
182
+ end
183
+
184
+ should 'run if prerequisite has run successfully' do
185
+ success_value = "1"
186
+ entry = @google_agent.get_entry
187
+ entry.update( @prerequisite_cell_name => success_value )
188
+ @testing_page.save
189
+
190
+ subroutine_ran = false
191
+ process_ran = @google_agent.process! do |entry|
192
+ subroutine_ran = true
193
+ true
194
+ end
195
+
196
+ assert process_ran, 'process! should return true'
197
+ assert subroutine_ran, 'The subref should have run'
198
+ entry = @google_agent.get_entry
199
+ assert_equal success_value, entry[@prerequisite_cell_name]
200
+ assert_equal "1", entry[@agent_name]
201
+ end
202
+
203
+ end #that requires a prerequisite
204
+
205
+ context 'max_selves' do
206
+ setup do
207
+ @agent_name = 'maxselftest'
208
+ @allowed_selves = 3
209
+ @google_agents = { }
210
+ row = 2
211
+ 4.times do
212
+ @keys['testentry'] = "test#{ row }"
213
+ @google_agents[@keys['testentry']] = prepare_google_agent_for(@agent_name, true, row, { :max_selves => @allowed_selves } )
214
+ row += 1
215
+ end
216
+ @command = 'ruby -e ' + "'" + '$0 = "%s"; sleep 120;' + "'"
217
+ @command %= File.basename $0
218
+ end
219
+
220
+ should 'set max_selves on agent' do
221
+ row = 2
222
+ 4.times do
223
+ assert_not_nil @google_agents["test#{ row }"].max_selves
224
+ assert_equal @allowed_selves, @google_agents["test#{ row }"].max_selves
225
+ row += 1
226
+ end
227
+ end
228
+
229
+ should 'not allow more than max_selves agents of the same name to run' do
230
+ row = 2
231
+ num_running = 0
232
+ @allowed_selves.times do
233
+ assert num_running < @google_agents[ "test#{ row }" ].max_selves, "#{ num_running } is greater than #{ @google_agents[ "test#{ row }" ].max_selves }!"
234
+
235
+ subroutine_ran = false
236
+ process_ran = @google_agents[ "test#{ row }" ].process! do |entry|
237
+ subroutine_ran = true
238
+ true
239
+ end
240
+
241
+ assert process_ran, 'process should return true'
242
+ assert subroutine_ran, 'The subref should have run'
243
+ entry = @google_agents[ "test#{ row }" ].get_entry
244
+ assert_equal "1", entry[@agent_name], "#{ @agent_name } field should have completed successfully"
245
+
246
+ system("#{ @command } &")
247
+ row += 1
248
+ num_running += 1
249
+ end
250
+ assert_equal num_running, @google_agents[ "test#{ row }" ].max_selves
251
+
252
+ subroutine_ran = false
253
+ process_ran = @google_agents[ "test#{ row }" ].process! do |entry|
254
+ subroutine_ran = true
255
+ true
256
+ end
257
+
258
+ assert process_ran, 'process should return true'
259
+ assert !subroutine_ran, 'The subref should not have run'
260
+ entry = @google_agents[ "test#{ row }" ].get_entry
261
+ assert entry[@agent_name].empty?, "#{ @agent_name } field should still be empty"
262
+ end
263
+
264
+ end #max_selves
265
+
266
+ context 'conflicts_with' do
267
+
268
+ setup do
269
+ @agent_name = 'conflictswithtest'
270
+ @max_conflicters = 2
271
+ @conflicting_script = 'conflicting_agent.rb'
272
+ @conflicts_with = { @conflicting_script => @max_conflicters }
273
+ @google_agent = prepare_google_agent_for(@agent_name, true, nil, { :conflicts_with => @conflicts_with } )
274
+ @command = 'ruby -e ' + "'" + '$0 = "%s"; sleep 60;' + "'"
275
+ @command %= @conflicting_script
276
+ end
277
+
278
+ should 'set conflicts_with on agent' do
279
+ assert_not_nil @google_agent.conflicts_with
280
+ assert @google_agent.conflicts_with.has_key? @conflicting_script
281
+ assert_equal @max_conflicters, @google_agent.conflicts_with[@conflicting_script]
282
+ end
283
+
284
+ should 'only run if a sufficiently low number of conflicting scripts are running' do
285
+ num_running = 0
286
+ @max_conflicters.times do
287
+ system("#{ @command } &")
288
+ num_running += 1
289
+
290
+ subroutine_ran = false
291
+ process_ran = @google_agent.process! do |entry|
292
+ subroutine_ran = true
293
+ true
294
+ end
295
+ assert process_ran, 'process should return true'
296
+ entry = @google_agent.get_entry
297
+
298
+ if num_running < @google_agent.conflicts_with[@conflicting_script]
299
+ assert subroutine_ran, "The subref should have run on the #{ num_running }th time"
300
+ assert_equal "1", entry[@agent_name], "#{ @agent_name } field should have completed successfully"
301
+ else
302
+ assert !subroutine_ran, 'The subref should not have run'
303
+ assert entry[@agent_name].empty?, "#{ @agent_name } field should still be empty, got #{ entry[@agent_name ] }"
304
+ end
305
+
306
+ @testing_page[2,4] = nil
307
+ @testing_page.save
308
+ end
309
+ assert_equal num_running, @google_agent.conflicts_with[@conflicting_script]
310
+ end
311
+
312
+ end #conflicts_with
313
+
314
+ context 'subsumes' do
315
+ setup do
316
+ @agent_name = 'subsumingagent'
317
+ @subsumed_cell_name = 'subsumedcell'
318
+ @subsumes = [ @subsumed_cell_name ]
319
+ @google_agent = prepare_google_agent_for(@agent_name, true, nil, { :subsumes => @subsumes } )
320
+ add_header_to_page(@subsumed_cell_name, @testing_page)
321
+ end
322
+
323
+ should 'set subsumes on agent' do
324
+ assert_not_nil @google_agent.subsumes
325
+ assert_equal @subsumes.count, @google_agent.subsumes.count
326
+ @subsumes.each do |subsumed|
327
+ assert @google_agent.subsumes.include?(subsumed), "#{ subsumed } should be a a subsumed field"
328
+ end
329
+ end
330
+
331
+ should 'update subsumed cell if run completes successfully' do
332
+ entry = @google_agent.get_entry
333
+ assert entry[@agent_name].empty?, "#{ @agent_name } field should be empty"
334
+ assert entry[@subsumed_cell_name].empty?, "#{ @subsumed_cell_name } field should be empty"
335
+
336
+ success_value = "1"
337
+ subroutine_ran = false
338
+ process_ran = @google_agent.process! do |entry|
339
+ # this should not run at all
340
+ subroutine_ran = true
341
+ true
342
+ end
343
+
344
+ assert process_ran, 'process! should return true'
345
+ assert subroutine_ran, 'The subref should have run'
346
+ entry = @google_agent.get_entry
347
+ assert_equal success_value, entry[@agent_name]
348
+ assert_equal success_value, entry[@subsumed_cell_name]
349
+ end
350
+
351
+ end #subsumes
352
+
353
+ context 'config_file' do
354
+
355
+ setup do
356
+ @agent_name = 'configagent'
357
+ @test_config_file = File.expand_path( File.dirname(__FILE__) + '/../config/test.config.yml' )
358
+ end
359
+
360
+ should 'be overridable' do
361
+ assert File.exists?(@test_config_file), "#{ @test_config_file } does not exist!"
362
+ test_config = Psych.load_file(@test_config_file)
363
+ @google_agent = prepare_google_agent_for(@agent_name, true, nil, { :config_file => @test_config_file } )
364
+ assert_equal @test_config_file, @google_agent.config_file
365
+ test_config.keys.each do |tkey|
366
+ assert @google_agent.config.has_key? tkey
367
+ assert_equal test_config[tkey], @google_agent.config[tkey]
368
+ end
369
+ end
370
+
371
+ end #config_file
372
+
373
+ end #Agent
374
+
375
+ def prepare_google_agent_for(agent, ready, agent_row = nil, extra_params = nil)
376
+ init_params = {
377
+ :agent_name => agent,
378
+ :page_name => @testing_page_name,
379
+ :keys => @keys,
380
+ :debug => true,
381
+ :config_file => @config_file
382
+ }
383
+ unless extra_params.nil?
384
+ init_params.merge!(extra_params)
385
+ end
386
+
387
+ google_agent = SpreadsheetAgent::Agent.new(init_params)
388
+ @testing_page = google_agent.worksheet
389
+
390
+ colnum = 1
391
+ ['testentry','testpage','ready', agent, 'complete'].each do |field|
392
+ @testing_page[1,colnum] = field
393
+ colnum += 1
394
+ end
395
+ if agent_row.nil?
396
+ agent_row = 2
397
+ end
398
+
399
+ @testing_page[agent_row,1] = @keys['testentry']
400
+ @testing_page[agent_row,2] = @keys['testpage']
401
+ @testing_page[agent_row,3] = "1" if ready
402
+ @testing_page.save
403
+ google_agent
404
+ end
405
+
406
+ def add_header_to_page(header, page)
407
+ page[1, page.num_cols + 1] = header
408
+ page.save
409
+ end
410
+
411
+ end #TC_SpreadsheetAgentTest
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spreadsheet_agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Darin London
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: google_drive
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: capture_io
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '0.1'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '0.1'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mail
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.5'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.5'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.2
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.2
78
+ - !ruby/object:Gem::Dependency
79
+ name: shoulda-context
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.1'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.1'
94
+ description: ! 'SpreadsheetAgent is a framework for creating distributed pipelines
95
+ across many
96
+
97
+ different servers. It is extensible, and flexible. It does not specify what goals
98
+
99
+ any pipeline should be working towards, or which goals are prerequisites for
100
+
101
+ other goals, but it does provide logic for easily defining these relationships
102
+
103
+ based on your own needs. It does this by providing a subsumption architecture,
104
+
105
+ whereby many small, highly focused agents are written to perform specific goals,
106
+
107
+ and also know what prerequisites and resources they require to perform them.
108
+
109
+ In addition, it is designed from the beginning to support the creation of simple
110
+
111
+ human-computational workflows.
112
+
113
+ '
114
+ email: darin.london@duke.edu
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - lib/spreadsheet_agent/db.rb
120
+ - lib/spreadsheet_agent/error.rb
121
+ - lib/spreadsheet_agent/runner.rb
122
+ - lib/spreadsheet_agent.rb
123
+ - test/agent_bin/othergoal_agent.rb
124
+ - test/agent_bin/testgoal_agent.rb
125
+ - test/spreadsheet_agent_db_test.rb
126
+ - test/spreadsheet_agent_runner_test.rb
127
+ - test/spreadsheet_agent_test.rb
128
+ homepage: http://rubygems.org/gems/spreadsheet_agent
129
+ licenses:
130
+ - MIT
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 1.8.24
150
+ signing_key:
151
+ specification_version: 3
152
+ summary: SpreadsheetAgent is a framework for creating distributed pipelines across
153
+ many different servers, each using the same google spreadsheet as a control panel.
154
+ test_files:
155
+ - test/spreadsheet_agent_db_test.rb
156
+ - test/spreadsheet_agent_runner_test.rb
157
+ - test/spreadsheet_agent_test.rb