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.
- data/.gitignore +19 -0
- data/.simplecov +8 -0
- data/.travis.yml +8 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +61 -0
- data/Rakefile +18 -0
- data/lib/structx.rb +169 -0
- data/lib/structx/version.rb +4 -0
- data/structx.gemspec +29 -0
- data/test/compatibility-test.rb +32 -0
- data/test/ruby/1.9/envutil.rb +212 -0
- data/test/ruby/1.9/test_struct.rb +252 -0
- data/test/ruby/2.0/envutil.rb +362 -0
- data/test/ruby/2.0/test_struct.rb +284 -0
- data/test/run.sh +12 -0
- data/test/spec_structx.rb +193 -0
- metadata +166 -0
@@ -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
|