structx 0.1.0

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,252 @@
1
+ require 'test/unit'
2
+ require 'timeout'
3
+
4
+ class TestStruct < Test::Unit::TestCase
5
+ def test_struct
6
+ struct_test = Struct.new("Test", :foo, :bar)
7
+ assert_equal(Struct::Test, struct_test)
8
+
9
+ test = struct_test.new(1, 2)
10
+ assert_equal(1, test.foo)
11
+ assert_equal(2, test.bar)
12
+ assert_equal(1, test[0])
13
+ assert_equal(2, test[1])
14
+
15
+ a, b = test.to_a
16
+ assert_equal(1, a)
17
+ assert_equal(2, b)
18
+
19
+ test[0] = 22
20
+ assert_equal(22, test.foo)
21
+
22
+ test.bar = 47
23
+ assert_equal(47, test.bar)
24
+ end
25
+
26
+ # [ruby-dev:26247] more than 10 struct members causes segmentation fault
27
+ def test_morethan10members
28
+ list = %w( a b c d e f g h i j k l m n o p )
29
+ until list.empty?
30
+ c = Struct.new(* list.map {|ch| ch.intern }).new
31
+ list.each do |ch|
32
+ c.__send__(ch)
33
+ end
34
+ list.pop
35
+ end
36
+ end
37
+
38
+ def test_small_structs
39
+ names = [:a, :b, :c, :d]
40
+ 1.upto(4) {|n|
41
+ fields = names[0, n]
42
+ klass = Struct.new(*fields)
43
+ o = klass.new(*(0...n).to_a)
44
+ fields.each_with_index {|name, i|
45
+ assert_equal(i, o[name])
46
+ }
47
+ o = klass.new(*(0...n).to_a.reverse)
48
+ fields.each_with_index {|name, i|
49
+ assert_equal(n-i-1, o[name])
50
+ }
51
+ }
52
+ end
53
+
54
+ def test_inherit
55
+ klass = Struct.new(:a)
56
+ klass2 = Class.new(klass)
57
+ o = klass2.new(1)
58
+ assert_equal(1, o.a)
59
+ end
60
+
61
+ def test_members
62
+ klass = Struct.new(:a)
63
+ o = klass.new(1)
64
+ assert_equal([:a], klass.members)
65
+ assert_equal([:a], o.members)
66
+ end
67
+
68
+ def test_ref
69
+ klass = Struct.new(:a)
70
+ o = klass.new(1)
71
+ assert_equal(1, o[:a])
72
+ assert_raise(NameError) { o[:b] }
73
+ end
74
+
75
+ def test_modify
76
+ klass = Struct.new(:a)
77
+ o = klass.new(1)
78
+ assert_raise(SecurityError) do
79
+ Thread.new do
80
+ $SAFE = 4
81
+ o.a = 2
82
+ end.value
83
+ end
84
+ end
85
+
86
+ def test_set
87
+ klass = Struct.new(:a)
88
+ o = klass.new(1)
89
+ o[:a] = 2
90
+ assert_equal(2, o[:a])
91
+ assert_raise(NameError) { o[:b] = 3 }
92
+ end
93
+
94
+ def test_struct_new
95
+ assert_raise(NameError) { Struct.new("foo") }
96
+ assert_nothing_raised { Struct.new("Foo") }
97
+ Struct.instance_eval { remove_const(:Foo) }
98
+ assert_nothing_raised { Struct.new(:a) { } }
99
+ assert_raise(RuntimeError) { Struct.new(:a) { raise } }
100
+
101
+ assert_equal([:utime, :stime, :cutime, :cstime], Process.times.members)
102
+ end
103
+
104
+ def test_initialize
105
+ klass = Struct.new(:a)
106
+ assert_raise(ArgumentError) { klass.new(1, 2) }
107
+ end
108
+
109
+ def test_each
110
+ klass = Struct.new(:a, :b)
111
+ o = klass.new(1, 2)
112
+ assert_equal([1, 2], o.each.to_a)
113
+ end
114
+
115
+ def test_each_pair
116
+ klass = Struct.new(:a, :b)
117
+ o = klass.new(1, 2)
118
+ assert_equal([[:a, 1], [:b, 2]], o.each_pair.to_a)
119
+ end
120
+
121
+ def test_inspect
122
+ klass = Struct.new(:a)
123
+ o = klass.new(1)
124
+ assert_equal("#<struct a=1>", o.inspect)
125
+ o.a = o
126
+ assert_match(/^#<struct a=#<struct #<.*?>:...>>$/, o.inspect)
127
+
128
+ Struct.new("Foo", :a)
129
+ o = Struct::Foo.new(1)
130
+ assert_equal("#<struct Struct::Foo a=1>", o.inspect)
131
+ Struct.instance_eval { remove_const(:Foo) }
132
+
133
+ klass = Struct.new(:a, :b)
134
+ o = klass.new(1, 2)
135
+ assert_equal("#<struct a=1, b=2>", o.inspect)
136
+
137
+ klass = Struct.new(:@a)
138
+ o = klass.new(1)
139
+ assert_equal("#<struct :@a=1>", o.inspect)
140
+ end
141
+
142
+ def test_init_copy
143
+ klass = Struct.new(:a)
144
+ o = klass.new(1)
145
+ assert_equal(o, o.dup)
146
+ end
147
+
148
+ def test_aref
149
+ klass = Struct.new(:a)
150
+ o = klass.new(1)
151
+ assert_equal(1, o[0])
152
+ assert_raise(IndexError) { o[-2] }
153
+ assert_raise(IndexError) { o[1] }
154
+ end
155
+
156
+ def test_aset
157
+ klass = Struct.new(:a)
158
+ o = klass.new(1)
159
+ o[0] = 2
160
+ assert_equal(2, o[:a])
161
+ assert_raise(IndexError) { o[-2] = 3 }
162
+ assert_raise(IndexError) { o[1] = 3 }
163
+ end
164
+
165
+ def test_values_at
166
+ klass = Struct.new(:a, :b, :c, :d, :e, :f)
167
+ o = klass.new(1, 2, 3, 4, 5, 6)
168
+ assert_equal([2, 4, 6], o.values_at(1, 3, 5))
169
+ assert_equal([2, 3, 4, 3, 4, 5], o.values_at(1..3, 2...5))
170
+ end
171
+
172
+ def test_select
173
+ klass = Struct.new(:a, :b, :c, :d, :e, :f)
174
+ o = klass.new(1, 2, 3, 4, 5, 6)
175
+ assert_equal([1, 3, 5], o.select {|v| v % 2 != 0 })
176
+ assert_raise(ArgumentError) { o.select(1) }
177
+ end
178
+
179
+ def test_equal
180
+ klass1 = Struct.new(:a)
181
+ klass2 = Struct.new(:a, :b)
182
+ o1 = klass1.new(1)
183
+ o2 = klass1.new(1)
184
+ o3 = klass2.new(1)
185
+ assert(o1.==(o2))
186
+ assert(o1 != o3)
187
+ end
188
+
189
+ def test_hash
190
+ klass = Struct.new(:a)
191
+ o = klass.new(1)
192
+ assert(o.hash.is_a?(Fixnum))
193
+ end
194
+
195
+ def test_eql
196
+ klass1 = Struct.new(:a)
197
+ klass2 = Struct.new(:a, :b)
198
+ o1 = klass1.new(1)
199
+ o2 = klass1.new(1)
200
+ o3 = klass2.new(1)
201
+ assert(o1.eql?(o2))
202
+ assert(!(o1.eql?(o3)))
203
+ end
204
+
205
+ def test_size
206
+ klass = Struct.new(:a)
207
+ o = klass.new(1)
208
+ assert_equal(1, o.size)
209
+ end
210
+
211
+ def test_error
212
+ assert_raise(TypeError){
213
+ Struct.new(0)
214
+ }
215
+ end
216
+
217
+ def test_nonascii
218
+ struct_test = Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
219
+ assert_equal(Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]')
220
+ a = struct_test.new(42)
221
+ assert_equal("#<struct Struct::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]')
222
+ end
223
+
224
+ def test_comparison_when_recursive
225
+ klass1 = Struct.new(:a, :b, :c)
226
+
227
+ x = klass1.new(1, 2, nil); x.c = x
228
+ y = klass1.new(1, 2, nil); y.c = y
229
+ Timeout.timeout(1) {
230
+ assert x == y
231
+ assert x.eql? y
232
+ }
233
+
234
+ z = klass1.new(:something, :other, nil); z.c = z
235
+ Timeout.timeout(1) {
236
+ assert x != z
237
+ assert !x.eql?(z)
238
+ }
239
+
240
+ x.c = y; y.c = x
241
+ Timeout.timeout(1) {
242
+ assert x == y
243
+ assert x.eql?(y)
244
+ }
245
+
246
+ x.c = z; z.c = x
247
+ Timeout.timeout(1) {
248
+ assert x != z
249
+ assert !x.eql?(z)
250
+ }
251
+ end
252
+ end
@@ -0,0 +1,362 @@
1
+ # -*- coding: us-ascii -*-
2
+ require "open3"
3
+ require "timeout"
4
+
5
+ module EnvUtil
6
+ def rubybin
7
+ unless ENV["RUBYOPT"]
8
+
9
+ end
10
+ if ruby = ENV["RUBY"]
11
+ return ruby
12
+ end
13
+ ruby = "ruby"
14
+ rubyexe = ruby+".exe"
15
+ 3.times do
16
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
17
+ return File.expand_path(ruby)
18
+ end
19
+ if File.exist? rubyexe and File.executable? rubyexe
20
+ return File.expand_path(rubyexe)
21
+ end
22
+ ruby = File.join("..", ruby)
23
+ end
24
+ if defined?(RbConfig.ruby)
25
+ RbConfig.ruby
26
+ else
27
+ "ruby"
28
+ end
29
+ end
30
+ module_function :rubybin
31
+
32
+ LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
33
+
34
+ def invoke_ruby(args, stdin_data="", capture_stdout=false, capture_stderr=false, opt={})
35
+ in_c, in_p = IO.pipe
36
+ out_p, out_c = IO.pipe if capture_stdout
37
+ err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
38
+ opt = opt.dup
39
+ opt[:in] = in_c
40
+ opt[:out] = out_c if capture_stdout
41
+ opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
42
+ if enc = opt.delete(:encoding)
43
+ out_p.set_encoding(enc) if out_p
44
+ err_p.set_encoding(enc) if err_p
45
+ end
46
+ timeout = opt.delete(:timeout) || 10
47
+ reprieve = opt.delete(:reprieve) || 1
48
+ c = "C"
49
+ child_env = {}
50
+ LANG_ENVS.each {|lc| child_env[lc] = c}
51
+ if Array === args and Hash === args.first
52
+ child_env.update(args.shift)
53
+ end
54
+ args = [args] if args.kind_of?(String)
55
+ pid = spawn(child_env, EnvUtil.rubybin, *args, opt)
56
+ in_c.close
57
+ out_c.close if capture_stdout
58
+ err_c.close if capture_stderr && capture_stderr != :merge_to_stdout
59
+ if block_given?
60
+ return yield in_p, out_p, err_p, pid
61
+ else
62
+ th_stdout = Thread.new { out_p.read } if capture_stdout
63
+ th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
64
+ in_p.write stdin_data.to_str
65
+ in_p.close
66
+ if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
67
+ stdout = th_stdout.value if capture_stdout
68
+ stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
69
+ else
70
+ signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
71
+ begin
72
+ Process.kill signal, pid
73
+ rescue Errno::ESRCH
74
+ break
75
+ else
76
+ end until signal == :KILL or (sleep reprieve; signal = :KILL; false)
77
+ raise Timeout::Error
78
+ end
79
+ out_p.close if capture_stdout
80
+ err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
81
+ Process.wait pid
82
+ status = $?
83
+ return stdout, stderr, status
84
+ end
85
+ ensure
86
+ [th_stdout, th_stderr].each do |th|
87
+ th.kill if th
88
+ end
89
+ [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
90
+ io.close if io && !io.closed?
91
+ end
92
+ [th_stdout, th_stderr].each do |th|
93
+ th.join if th
94
+ end
95
+ end
96
+ module_function :invoke_ruby
97
+
98
+ alias rubyexec invoke_ruby
99
+ class << self
100
+ alias rubyexec invoke_ruby
101
+ end
102
+
103
+ def verbose_warning
104
+ class << (stderr = "")
105
+ alias write <<
106
+ end
107
+ stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
108
+ yield stderr
109
+ return $stderr
110
+ ensure
111
+ stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
112
+ end
113
+ module_function :verbose_warning
114
+
115
+ def suppress_warning
116
+ verbose, $VERBOSE = $VERBOSE, nil
117
+ yield
118
+ ensure
119
+ $VERBOSE = verbose
120
+ end
121
+ module_function :suppress_warning
122
+
123
+ def under_gc_stress
124
+ stress, GC.stress = GC.stress, true
125
+ yield
126
+ ensure
127
+ GC.stress = stress
128
+ end
129
+ module_function :under_gc_stress
130
+ end
131
+
132
+ module Test
133
+ module Unit
134
+ module Assertions
135
+ public
136
+ def assert_valid_syntax(code, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
137
+ code = code.dup.force_encoding("ascii-8bit")
138
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
139
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
140
+ }
141
+ code.force_encoding("us-ascii")
142
+ verbose, $VERBOSE = $VERBOSE, nil
143
+ yield if defined?(yield)
144
+ case
145
+ when Array === fname
146
+ fname, line = *fname
147
+ when defined?(fname.path) && defined?(fname.lineno)
148
+ fname, line = fname.path, fname.lineno
149
+ else
150
+ line = 0
151
+ end
152
+ assert_nothing_raised(SyntaxError, mesg) do
153
+ assert_equal(:ok, catch {|tag| eval(code, binding, fname, line)}, mesg)
154
+ end
155
+ ensure
156
+ $VERBOSE = verbose
157
+ end
158
+
159
+ def assert_syntax_error(code, error, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
160
+ code = code.dup.force_encoding("ascii-8bit")
161
+ code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
162
+ "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ng}\n"
163
+ }
164
+ code.force_encoding("us-ascii")
165
+ verbose, $VERBOSE = $VERBOSE, nil
166
+ yield if defined?(yield)
167
+ case
168
+ when Array === fname
169
+ fname, line = *fname
170
+ when defined?(fname.path) && defined?(fname.lineno)
171
+ fname, line = fname.path, fname.lineno
172
+ else
173
+ line = 0
174
+ end
175
+ e = assert_raise(SyntaxError, mesg) do
176
+ catch {|tag| eval(code, binding, fname, line)}
177
+ end
178
+ assert_match(error, e.message, mesg)
179
+ ensure
180
+ $VERBOSE = verbose
181
+ end
182
+
183
+ def assert_normal_exit(testsrc, message = '', opt = {})
184
+ assert_valid_syntax(testsrc, caller_locations(1, 1)[0])
185
+ if opt.include?(:child_env)
186
+ opt = opt.dup
187
+ child_env = [opt.delete(:child_env)] || []
188
+ else
189
+ child_env = []
190
+ end
191
+ out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, opt)
192
+ assert !status.signaled?, FailDesc[status, message, out]
193
+ end
194
+
195
+ FailDesc = proc do |status, message = "", out = ""|
196
+ pid = status.pid
197
+ faildesc = proc do
198
+ signo = status.termsig
199
+ signame = Signal.list.invert[signo]
200
+ sigdesc = "signal #{signo}"
201
+ if signame
202
+ sigdesc = "SIG#{signame} (#{sigdesc})"
203
+ end
204
+ if status.coredump?
205
+ sigdesc << " (core dumped)"
206
+ end
207
+ full_message = ''
208
+ if message and !message.empty?
209
+ full_message << message << "\n"
210
+ end
211
+ full_message << "pid #{pid} killed by #{sigdesc}"
212
+ if out and !out.empty?
213
+ full_message << "\n#{out.gsub(/^/, '| ')}"
214
+ full_message << "\n" if /\n\z/ !~ full_message
215
+ end
216
+ full_message
217
+ end
218
+ faildesc
219
+ end
220
+
221
+ def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, opt={})
222
+ stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, opt)
223
+ if block_given?
224
+ raise "test_stdout ignored, use block only or without block" if test_stdout != []
225
+ raise "test_stderr ignored, use block only or without block" if test_stderr != []
226
+ yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status)
227
+ else
228
+ errs = []
229
+ [[test_stdout, stdout], [test_stderr, stderr]].each do |exp, act|
230
+ begin
231
+ if exp.is_a?(Regexp)
232
+ assert_match(exp, act, message)
233
+ else
234
+ assert_equal(exp, act.lines.map {|l| l.chomp }, message)
235
+ end
236
+ rescue MiniTest::Assertion => e
237
+ errs << e.message
238
+ message = nil
239
+ end
240
+ end
241
+ raise MiniTest::Assertion, errs.join("\n---\n") unless errs.empty?
242
+ status
243
+ end
244
+ end
245
+
246
+ def assert_ruby_status(args, test_stdin="", message=nil, opt={})
247
+ out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, opt)
248
+ message ||= "ruby exit status is not success:"
249
+ assert(status.success?, FailDesc[status, message, out])
250
+ end
251
+
252
+ ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV")
253
+
254
+ def assert_separately(args, file = nil, line = nil, src, **opt)
255
+ unless file and line
256
+ loc, = caller_locations(1,1)
257
+ file ||= loc.path
258
+ line ||= loc.lineno
259
+ end
260
+ src = <<eom
261
+ require 'test/unit';include Test::Unit::Assertions;begin;#{src}
262
+ ensure
263
+ puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}"
264
+ end
265
+ eom
266
+ stdout, stderr, status = EnvUtil.invoke_ruby(args, src, true, true, opt)
267
+ abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
268
+ assert(!abort, FailDesc[status, stderr])
269
+ self._assertions += stdout[/^assertions=(\d+)/, 1].to_i
270
+ res = Marshal.load(stdout.unpack("m")[0])
271
+ return unless res
272
+ res.backtrace.each do |l|
273
+ l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
274
+ end
275
+ raise res
276
+ end
277
+
278
+ def assert_warning(pat, message = nil)
279
+ stderr = EnvUtil.verbose_warning { yield }
280
+ message = ' "' + message + '"' if message
281
+ msg = proc {"warning message #{stderr.inspect} is expected to match #{pat.inspect}#{message}"}
282
+ assert(pat === stderr, msg)
283
+ end
284
+
285
+ def assert_warn(*args)
286
+ assert_warning(*args) {$VERBOSE = false; yield}
287
+ end
288
+
289
+ def assert_no_memory_leak(args, prepare, code, message=nil, limit: 1.5)
290
+ token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m"
291
+ token_dump = token.dump
292
+ token_re = Regexp.quote(token)
293
+ args = [
294
+ "--disable=gems",
295
+ "-r", File.expand_path("../memory_status", __FILE__),
296
+ *args,
297
+ "-v", "-",
298
+ ]
299
+ cmd = [
300
+ 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new.size}"}',
301
+ prepare,
302
+ 'STDERR.puts('"#{token_dump}"'"START=#{$initial_size = Memory::Status.new.size}")',
303
+ code,
304
+ ].join("\n")
305
+ _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true)
306
+ before = err.sub!(/^#{token_re}START=(\d+)\n/, '') && $1.to_i
307
+ after = err.sub!(/^#{token_re}FINAL=(\d+)\n/, '') && $1.to_i
308
+ assert_equal([true, ""], [status.success?, err], message)
309
+ assert_operator(after.fdiv(before), :<, limit, message)
310
+ end
311
+
312
+ def assert_is_minus_zero(f)
313
+ assert(1.0/f == -Float::INFINITY, "#{f} is not -0.0")
314
+ end
315
+
316
+ def assert_file
317
+ AssertFile
318
+ end
319
+
320
+ class << (AssertFile = Struct.new(:message).new)
321
+ include Assertions
322
+ def assert_file_predicate(predicate, *args)
323
+ if /\Anot_/ =~ predicate
324
+ predicate = $'
325
+ neg = " not"
326
+ end
327
+ result = File.__send__(predicate, *args)
328
+ result = !result if neg
329
+ mesg = "Expected file " << args.shift.inspect
330
+ mesg << mu_pp(args) unless args.empty?
331
+ mesg << "#{neg} to be #{predicate}"
332
+ mesg << " #{message}" if message
333
+ assert(result, mesg)
334
+ end
335
+ alias method_missing assert_file_predicate
336
+
337
+ def for(message)
338
+ clone.tap {|a| a.message = message}
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
344
+
345
+ begin
346
+ require 'rbconfig'
347
+ rescue LoadError
348
+ else
349
+ module RbConfig
350
+ @ruby = EnvUtil.rubybin
351
+ class << self
352
+ undef ruby if method_defined?(:ruby)
353
+ attr_reader :ruby
354
+ end
355
+ dir = File.dirname(ruby)
356
+ name = File.basename(ruby, CONFIG['EXEEXT'])
357
+ CONFIG['bindir'] = dir
358
+ CONFIG['ruby_install_name'] = name
359
+ CONFIG['RUBY_INSTALL_NAME'] = name
360
+ Gem::ConfigMap[:bindir] = dir if defined?(Gem::ConfigMap)
361
+ end
362
+ end