spreadsheet_agent 0.0.1

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