ruby_ex 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/AUTHORS +51 -0
  2. data/ChangeLog +1763 -0
  3. data/NEWS +3 -0
  4. data/README +1 -0
  5. data/Rakefile +8 -0
  6. data/SPEC.dyn.yml +10 -0
  7. data/SPEC.gem.yml +269 -0
  8. data/SPEC.yml +36 -0
  9. data/src/abstract.rb +253 -0
  10. data/src/abstract_node.rb +85 -0
  11. data/src/algorithms.rb +12 -0
  12. data/src/algorithms/simulated_annealing.rb +142 -0
  13. data/src/ask.rb +100 -0
  14. data/src/attributed_class.rb +303 -0
  15. data/src/cache.rb +350 -0
  16. data/src/checkout.rb +12 -0
  17. data/src/choose.rb +271 -0
  18. data/src/commands.rb +20 -0
  19. data/src/commands/command.rb +492 -0
  20. data/src/commands/datas.rb +16 -0
  21. data/src/commands/datas/composite.rb +31 -0
  22. data/src/commands/datas/data.rb +65 -0
  23. data/src/commands/datas/factory.rb +69 -0
  24. data/src/commands/datas/temp.rb +26 -0
  25. data/src/commands/factory.rb +67 -0
  26. data/src/commands/helpers.rb +81 -0
  27. data/src/commands/pipe.rb +66 -0
  28. data/src/commands/runners.rb +16 -0
  29. data/src/commands/runners/exec.rb +50 -0
  30. data/src/commands/runners/fork.rb +130 -0
  31. data/src/commands/runners/runner.rb +140 -0
  32. data/src/commands/runners/system.rb +57 -0
  33. data/src/commands/seq.rb +32 -0
  34. data/src/config_file.rb +95 -0
  35. data/src/const_regexp.rb +57 -0
  36. data/src/daemon.rb +135 -0
  37. data/src/diff.rb +665 -0
  38. data/src/dlogger.rb +62 -0
  39. data/src/drb/drb_observable.rb +95 -0
  40. data/src/drb/drb_observable_pool.rb +27 -0
  41. data/src/drb/drb_service.rb +44 -0
  42. data/src/drb/drb_undumped_attributes.rb +56 -0
  43. data/src/drb/drb_undumped_indexed_object.rb +55 -0
  44. data/src/drb/insecure_protected_methods.rb +101 -0
  45. data/src/drb_ex.rb +12 -0
  46. data/src/dumpable_proc.rb +57 -0
  47. data/src/filetype.rb +229 -0
  48. data/src/generate_id.rb +44 -0
  49. data/src/histogram.rb +222 -0
  50. data/src/hookable.rb +283 -0
  51. data/src/hooker.rb +54 -0
  52. data/src/indexed_node.rb +65 -0
  53. data/src/io_marshal.rb +99 -0
  54. data/src/ioo.rb +193 -0
  55. data/src/labeled_node.rb +62 -0
  56. data/src/logger_observer.rb +24 -0
  57. data/src/md5sum.rb +70 -0
  58. data/src/module/autoload_tree.rb +65 -0
  59. data/src/module/hierarchy.rb +334 -0
  60. data/src/module/instance_method_visibility.rb +71 -0
  61. data/src/node.rb +81 -0
  62. data/src/object_monitor.rb +143 -0
  63. data/src/object_monitor_activity.rb +34 -0
  64. data/src/observable.rb +138 -0
  65. data/src/observable_pool.rb +291 -0
  66. data/src/orderedhash.rb +252 -0
  67. data/src/pp_hierarchy.rb +30 -0
  68. data/src/random_generators.rb +29 -0
  69. data/src/random_generators/random_generator.rb +33 -0
  70. data/src/random_generators/ruby.rb +25 -0
  71. data/src/ruby_ex.rb +124 -0
  72. data/src/safe_eval.rb +346 -0
  73. data/src/sendmail.rb +214 -0
  74. data/src/service_manager.rb +122 -0
  75. data/src/shuffle.rb +30 -0
  76. data/src/spring.rb +134 -0
  77. data/src/spring_set.rb +134 -0
  78. data/src/symtbl.rb +108 -0
  79. data/src/synflow.rb +474 -0
  80. data/src/thread_mutex.rb +11 -0
  81. data/src/timeout_ex.rb +79 -0
  82. data/src/trace.rb +26 -0
  83. data/src/uri/druby.rb +78 -0
  84. data/src/uri/file.rb +63 -0
  85. data/src/uri/ftp_ex.rb +36 -0
  86. data/src/uri/http_ex.rb +41 -0
  87. data/src/uri/pgsql.rb +136 -0
  88. data/src/uri/ssh.rb +87 -0
  89. data/src/uri/svn.rb +113 -0
  90. data/src/uri_ex.rb +71 -0
  91. data/src/verbose_object.rb +70 -0
  92. data/src/yaml/basenode_ext.rb +63 -0
  93. data/src/yaml/chop_header.rb +24 -0
  94. data/src/yaml/transform.rb +450 -0
  95. data/src/yaml/yregexpath.rb +76 -0
  96. data/test/algorithms/simulated_annealing_test.rb +102 -0
  97. data/test/check-pkg-ruby_ex.yml +15 -0
  98. data/test/check-ruby_ex.yml +12 -0
  99. data/test/resources/autoload_tree/A.rb +11 -0
  100. data/test/resources/autoload_tree/B.rb +10 -0
  101. data/test/resources/autoload_tree/foo/C.rb +18 -0
  102. data/test/resources/foo.txt +6 -0
  103. data/test/sanity-suite.yml +12 -0
  104. data/test/sanity/multiple-requires.yml +20 -0
  105. data/test/sanity/single-requires.yml +24 -0
  106. data/test/test-unit-setup.rb +6 -0
  107. data/test/unit-suite.yml +14 -0
  108. metadata +269 -0
@@ -0,0 +1,30 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: polrop $
6
+ # $Id: hierarchy.rb 145 2005-01-30 14:16:14Z polrop $
7
+
8
+ require 'ruby_ex'
9
+
10
+ class Hash
11
+
12
+ # Pretty print a hash symbolizing a class hierarchy.
13
+ def pp_hierarchy(prefix=//, abstract=false)
14
+ pp_hierarchy_rec('', prefix, abstract)
15
+ end
16
+
17
+ protected
18
+ def pp_hierarchy_rec(indent, prefix, abstract)
19
+ self.each do |k, v|
20
+ k_str = k.to_s.sub(prefix, '')
21
+ if abstract and k.abstract?
22
+ puts "#{indent}/#{k_str}/"
23
+ else
24
+ puts "#{indent}#{k_str}"
25
+ end
26
+ v.pp_hierarchy_rec(indent + ' ', prefix, abstract)
27
+ end
28
+ end
29
+
30
+ end # class Hash
@@ -0,0 +1,29 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: random_generators.rb 266 2005-06-01 14:27:18Z ertai $
5
+
6
+ require 'ruby_ex'
7
+ require 'module/autoload_tree'
8
+ require 'abstract'
9
+
10
+ module RandomGenerators
11
+
12
+ autoloaded_module(__FILE__)
13
+
14
+ module_function
15
+
16
+ def default
17
+ class << self
18
+ def default
19
+ @@default
20
+ end
21
+ end
22
+ @@default = Ruby.new
23
+ end
24
+
25
+ def default= ( aClass )
26
+ @@default = aClass
27
+ end
28
+
29
+ end # module RandomGenerators
@@ -0,0 +1,33 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: random_generator.rb 258 2005-06-01 00:22:51Z ertai $
5
+
6
+ require 'random_generators'
7
+
8
+ module RandomGenerators
9
+
10
+ class RandomGenerator
11
+ abstract
12
+
13
+ def check_choose_integer ( n )
14
+ unless n.is_a? Integer
15
+ raise ArgumentError, "Need an integer (not #{n})"
16
+ end
17
+ if n <= 0
18
+ raise ArgumentError, "Need an positive value (not #{n})"
19
+ end
20
+ end
21
+
22
+ def check_choose_float ( f )
23
+ unless f.is_a? Float
24
+ raise ArgumentError, "Need a float (not #{f})"
25
+ end
26
+ if f <= 0
27
+ raise ArgumentError, "Need an positive value (not #{f})"
28
+ end
29
+ end
30
+
31
+ end # class RandomGenerator
32
+
33
+ end # module RandomGenerators
@@ -0,0 +1,25 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: ruby.rb 258 2005-06-01 00:22:51Z ertai $
5
+
6
+ require 'random_generators'
7
+
8
+ module RandomGenerators
9
+
10
+ class Ruby < RandomGenerator
11
+ concrete
12
+
13
+ def choose_integer ( n )
14
+ check_choose_integer(n)
15
+ Kernel.rand(n)
16
+ end
17
+
18
+ def choose_float ( f=1.0 )
19
+ check_choose_float(f)
20
+ Kernel.rand() * f
21
+ end
22
+
23
+ end # class Ruby
24
+
25
+ end # module RandomGenerators
data/src/ruby_ex.rb ADDED
@@ -0,0 +1,124 @@
1
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License:: Gnu General Public License.
4
+ # Revision:: $Id: ruby_ex.rb 266 2005-06-01 14:27:18Z ertai $
5
+
6
+ begin
7
+ require 'rubygems'
8
+ require_gem 'core_ex' # FIXME version
9
+ require 'core_ex'
10
+ rescue LoadError => ex
11
+ core_ex = @@dir.parent + 'core_ex/lib/core_ex'
12
+ if core_ex.exists? and not $".include? 'core_ex.rb'
13
+ require core_ex.to_s
14
+ else
15
+ raise ex
16
+ end
17
+ end
18
+
19
+
20
+ module DRb
21
+ end
22
+
23
+ module Algorithms
24
+ end
25
+
26
+ module RubyEx
27
+
28
+ @@dir = Pathname.new(__FILE__).dirname.expand_path
29
+ $: << @@dir.to_s
30
+
31
+ module_function
32
+
33
+ def sources
34
+ @@contents ||= Hash.new([].freeze)
35
+ %[
36
+ abstract.rb Abstract Concrete Class
37
+ abstract_node.rb AbstractNode
38
+ algorithms.rb Algorithms
39
+ attributed_class.rb AttributedClass
40
+ cache.rb Cache
41
+ config_file.rb ConfigFile
42
+ const_regexp.rb ConstRegexp
43
+ daemon.rb Daemon
44
+ diff.rb Diff
45
+ dlogger.rb DLogger
46
+ drb.rb DRb
47
+ dumpable_proc.rb DumpableProc
48
+ filetype.rb FileType
49
+ histogram.rb Histogram
50
+ hookable.rb Hookable
51
+ hooker.rb Hooker
52
+ inactive_timeout.rb InactiveTimeout
53
+ indexed_node.rb IndexedNode
54
+ ioo.rb IOO IOOTCPSocket
55
+ killall.rb KillAll
56
+ labeled_node.rb LabeledNode
57
+ node.rb Node
58
+ object_monitor.rb ObjectMonitor
59
+ object_monitor_activity.rb ObjectMonitorActivity
60
+ observable.rb Observable
61
+ observable_pool.rb ObservablePool
62
+ orderedhash.rb OrderedHash_ext OHash
63
+ pseudo_cache.rb PseudoCache
64
+ random_generators.rb RandomGenerators
65
+ timeout_ex.rb TimeoutEx
66
+ verbose_object.rb VerboseObject
67
+ spring.rb Spring
68
+ spring_set.rb SpringSet
69
+ symtbl.rb SymTbl
70
+ synflow.rb SynFlowFactory SynFlow
71
+ safe_eval.rb SafeEval
72
+ sendmail.rb Sendmail
73
+ service_manager.rb ServiceManager
74
+ test/unit/ui/yaml/testrunner.rb Test::Unit::UI::Yaml::TestRunner
75
+ # drb/session_manager.rb DRb::SessionManager
76
+ # drb/dispatcher.rb DRb::Dispatcher
77
+ ].split(/\n/).each do |line|
78
+ line.sub!(/#.*$/, '')
79
+ line.strip!
80
+ next if line =~ /^\s*$/
81
+ file, *names = line.split(/\s+/)
82
+ @@contents[@@dir + file] = names
83
+ end
84
+ @@contents.freeze
85
+
86
+ @@sources = FileList[@@dir + '**/*.rb'].exclude(/\bsession\/error.rb\b/)
87
+
88
+ class << self
89
+ def sources
90
+ @@sources
91
+ end
92
+ end
93
+ @@sources
94
+ end
95
+
96
+ def dir
97
+ @@dir
98
+ end
99
+
100
+ def each ( &block )
101
+ sources.each do |file|
102
+ block[file, @@contents[file]]
103
+ end
104
+ self
105
+ end
106
+
107
+ def autoload
108
+ each do |file, names|
109
+ names.each do |name|
110
+ name = name.split(/::/).first
111
+ Kernel.autoload(name, file.to_s)
112
+ end
113
+ end
114
+ end
115
+
116
+ def require
117
+ each { |file, names| file.require }
118
+ end
119
+
120
+ end # module RubyEx
121
+
122
+ # RubyEx.each { |*a| p a }
123
+ # RubyEx.require
124
+ # RubyEx.autoload
data/src/safe_eval.rb ADDED
@@ -0,0 +1,346 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: safe_eval.rb 266 2005-06-01 14:27:18Z ertai $
7
+
8
+ require 'ruby_ex'
9
+
10
+ class SafeEval
11
+
12
+ attr_accessor :collect, :safe
13
+ attr_reader :output
14
+
15
+ def initialize(safe=3, collect=true)
16
+ @safe = safe
17
+ @collect = collect
18
+ end
19
+
20
+ def run(code_str='', binding=nil, filename='(SafeEval)', lineno=1)
21
+ binding = yield if block_given?
22
+ lineno -= 1
23
+ @output = nil
24
+ @output = [] if @collect
25
+ result = nil
26
+ code_str.strip!
27
+ return result if code_str.empty?
28
+ rd_result, wr_result = IO.pipe
29
+ open('|-') do |io|
30
+ if io.nil? then # son
31
+ status = 0
32
+ rd_result.close
33
+ set_environment
34
+ begin
35
+ code = "$SAFE = #@safe\n#{code_str}"
36
+ result = eval(code, binding, filename, lineno)
37
+ status = 0 # needed if the user change the status
38
+ rescue Exception => exception
39
+ status = 1
40
+ result = exception
41
+ ensure
42
+ Marshal.dump(result, wr_result)
43
+ wr_result.close
44
+ exit!(status)
45
+ end
46
+ else # father
47
+ wr_result.close
48
+ io.each_line { |l| @output << l.chomp } if @collect
49
+ result = Marshal.load(rd_result)
50
+ rd_result.close
51
+ Process.waitpid(io.pid)
52
+ end
53
+ end
54
+ raise result if $?.exitstatus == 1
55
+ @output = nil unless @collect
56
+ return result
57
+ end
58
+ public :run
59
+
60
+ # Can be overloaded to specify environment restriction.
61
+ def set_environment
62
+ end
63
+ protected :set_environment
64
+
65
+ end # class SafeEval
66
+
67
+
68
+ #
69
+ # Unit test suite
70
+ #
71
+ test_section __FILE__ do
72
+
73
+
74
+ class SafeEvalTest < Test::Unit::TestCase
75
+
76
+ #
77
+ # Tests
78
+ #
79
+ def test_interface
80
+ se = SafeEval.new
81
+ assert_respond_to(se, :run)
82
+ assert_respond_to(se, :output)
83
+ assert_respond_to(se, :safe)
84
+ assert_respond_to(se, :collect)
85
+ assert(se.protected_methods.include?('set_environment'),
86
+ "no protected method 'set_environment'")
87
+ end
88
+
89
+ def test_nil
90
+ se = SafeEval.new
91
+ assert_equal(nil, se.run)
92
+ assert_equal([], se.output)
93
+ end
94
+
95
+ def test_true
96
+ se = SafeEval.new
97
+ assert_equal(true, se.run('true'))
98
+ assert_equal([], se.output)
99
+ end
100
+
101
+ def test_false
102
+ se = SafeEval.new
103
+ assert_equal(false, se.run('false'))
104
+ assert_equal([], se.output)
105
+ end
106
+
107
+ def test_nil_no_collect
108
+ se = SafeEval.new(3, false)
109
+ assert_equal(nil, se.run)
110
+ assert_equal(nil, se.output)
111
+ end
112
+
113
+ def test_almost_empty_code
114
+ se = SafeEval.new
115
+ assert_equal(nil, se.run(' '))
116
+ assert_equal([], se.output)
117
+ end
118
+
119
+ def test_true_no_collect
120
+ se = SafeEval.new(3, false)
121
+ assert_equal(true, se.run('true'))
122
+ assert_equal(nil, se.output)
123
+ end
124
+
125
+ def test_binding_argument
126
+ def getBinding; a = 42; return binding; end
127
+ se = SafeEval.new
128
+ assert_equal(nil, se.run('puts a', getBinding))
129
+ assert_equal(['42'], se.output)
130
+ end
131
+
132
+ def test_binding_block
133
+ se = SafeEval.new
134
+ assert_equal(true, se.run('puts a; true') { a = 42; return binding })
135
+ assert_equal(['42'], se.output)
136
+ end
137
+
138
+ def test_binding_both
139
+ def getBinding2; a = 51; return binding; end
140
+ se = SafeEval.new
141
+ ret = se.run('puts a; true', getBinding2) { a = 42; return binding }
142
+ assert_equal(true, ret)
143
+ assert_equal(['42'], se.output)
144
+ end
145
+
146
+ def test_exception
147
+ mypid = $$
148
+ se = SafeEval.new
149
+ $tmp = 42
150
+ begin
151
+ $tmp = se.run('raise NoMethodError')
152
+ rescue NoMethodError
153
+ assert_equal(mypid, $$, "bad pid")
154
+ assert_equal(42, $tmp, "result must be nil")
155
+ assert_equal([], se.output)
156
+ ensure
157
+ assert_equal(mypid, $$, "bad pid")
158
+ end
159
+ end
160
+
161
+ def test_exception_collect
162
+ se = SafeEval.new
163
+ assert_raises(Exception) do
164
+ se.run('puts "toto"; raise Exception')
165
+ end
166
+ assert_equal(['toto'], se.output)
167
+ end
168
+
169
+ # FIXME: this test block if DRb::SessionManager test suite is executed before
170
+ # this test suite in check-ruby_ex.
171
+ # def test_exception_no_collect
172
+ # se = SafeEval.new(3, false)
173
+ # assert_raises(Exception) do
174
+ # se.run('puts "toto"; raise Exception')
175
+ # end
176
+ # assert_equal(nil, se.output)
177
+ # end
178
+
179
+ def test_require_forbidden
180
+ se = SafeEval.new
181
+ assert_raises(SecurityError) do
182
+ se.run("require 'foo'")
183
+ end
184
+ assert_equal([], se.output)
185
+ end
186
+
187
+ def test_File_chmod_forbidden
188
+ se = SafeEval.new
189
+ assert_raises(SecurityError) do
190
+ se.run("File.chmod(0755, '/tmp')")
191
+ end
192
+ assert_equal([], se.output)
193
+ end
194
+
195
+ def test_system_forbidden
196
+ se = SafeEval.new
197
+ assert_raises(SecurityError) do
198
+ se.run("system('rm /tmp/x')")
199
+ end
200
+ assert_equal([], se.output)
201
+ end
202
+
203
+ def test_system_allowed
204
+ se = SafeEval.new(2)
205
+ se.run("system(\"echo 'toto'\")")
206
+ assert_equal(['toto'], se.output)
207
+ end
208
+
209
+ def test_print
210
+ se = SafeEval.new
211
+ assert_equal(nil, se.run('print "hello"'))
212
+ assert_equal(['hello'], se.output)
213
+ end
214
+
215
+ def test_puts
216
+ se = SafeEval.new
217
+ assert_equal(nil, se.run('puts "hello"'))
218
+ assert_equal(['hello'], se.output)
219
+ end
220
+
221
+ def test_print_no_collect
222
+ se = SafeEval.new(3, false)
223
+ assert_equal(nil, se.run('print "hello"'))
224
+ assert_equal(nil, se.output)
225
+ end
226
+
227
+ def test_puts_several_lines
228
+ se = SafeEval.new
229
+ assert_equal(nil, se.run('puts "hello\nworld"'))
230
+ assert_equal(['hello', 'world'], se.output)
231
+ end
232
+
233
+ def test_syntax_error
234
+ se = SafeEval.new
235
+ assert_raises(SyntaxError) do
236
+ se.run('4syntaxerror')
237
+ end
238
+ assert_equal([], se.output)
239
+ end
240
+
241
+ def test_bad_identifier
242
+ se = SafeEval.new
243
+ assert_raises(NameError) do
244
+ se.run('bad_identifier.directory?')
245
+ end
246
+ assert_equal([], se.output)
247
+ end
248
+
249
+ def test_syntax_error_location
250
+ se = SafeEval.new
251
+ exc = assert_raises(SyntaxError) do
252
+ se.run('4syntaxerror', nil, 'toto.yml', 1)
253
+ end
254
+ expected_message = <<EOF
255
+ compile error
256
+ toto.yml:1: syntax error
257
+ 4syntaxerror
258
+ ^
259
+ EOF
260
+ expected_message.chomp!
261
+ assert_equal(expected_message, exc.message)
262
+ end
263
+
264
+ def test_syntax_error_location_several_line
265
+ code = <<EOF
266
+ puts "hello"
267
+ puts "world"
268
+ 4syntaxerror;
269
+ EOF
270
+ se = SafeEval.new
271
+ exc = assert_raises(SyntaxError) do
272
+ se.run(code, nil, 'toto.yml', 1)
273
+ end
274
+ expected_message = <<EOF
275
+ compile error
276
+ toto.yml:3: syntax error
277
+ 4syntaxerror;
278
+ ^
279
+ EOF
280
+ expected_message.chomp!
281
+ assert_equal(expected_message, exc.message)
282
+ end
283
+
284
+ def test_exit_forbidden
285
+ se = SafeEval.new(3, false)
286
+ assert_raises(SystemExit) do
287
+ se.run('exit(42)')
288
+ end
289
+ assert_equal(nil, se.output)
290
+ end
291
+
292
+ def test_exit_strong_forbidden
293
+ se = SafeEval.new
294
+ assert_raises(EOFError) do
295
+ se.run('exit!(42)')
296
+ end
297
+ assert_equal([], se.output)
298
+ end
299
+
300
+ def test_undef_me
301
+ se = SafeEval.new
302
+ assert_raises(NoMethodError) do
303
+ se.run('undef_method :run')
304
+ end
305
+ end
306
+
307
+ def test_read_my_var
308
+ se = SafeEval.new
309
+ str = 'puts code'
310
+ se.run(str)
311
+ assert_equal(str, se.output[-1])
312
+ end
313
+
314
+ def test_write_status
315
+ se = SafeEval.new
316
+ assert_equal(1, se.run('status = 1'))
317
+ end
318
+
319
+ def test_write_safe
320
+ se = SafeEval.new
321
+ assert_equal(1, se.run('@safe = 1'))
322
+ assert_equal(true, se.run('@safe == 3'))
323
+ end
324
+
325
+ def test_write_output
326
+ se = SafeEval.new
327
+ assert_equal(['foo'], se.run('@output << "foo"'))
328
+ assert_equal([], se.output)
329
+ end
330
+
331
+ def test_write_output_and_print
332
+ se = SafeEval.new
333
+ assert_equal(nil, se.run('@output << "foo"; p @output'))
334
+ assert_equal(["[\"foo\"]"], se.output)
335
+ end
336
+
337
+ def test_sleep
338
+ se = SafeEval.new
339
+ assert(se.run('sleep(1)') >= 1)
340
+ assert_equal([], se.output)
341
+ end
342
+
343
+ end # class SafeEvalTest
344
+
345
+
346
+ end