Skip to content

Commit b4ae461

Browse files
committed
Update specs
PullRequest: truffleruby/632
2 parents a3df7c3 + 22078be commit b4ae461

File tree

159 files changed

+1777
-537
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+1777
-537
lines changed

spec/mspec/lib/mspec/guards/platform.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@ def self.standard?
1818
implementation? :ruby
1919
end
2020

21-
HOST_OS = begin
21+
PLATFORM = if RUBY_ENGINE == "jruby"
2222
require 'rbconfig'
23-
RbConfig::CONFIG['host_os'] || RUBY_PLATFORM
24-
rescue LoadError
23+
"#{RbConfig::CONFIG['host_cpu']}-#{RbConfig::CONFIG['host_os']}"
24+
else
2525
RUBY_PLATFORM
26-
end.downcase
26+
end
2727

2828
def self.os?(*oses)
2929
oses.any? do |os|
3030
raise ":java is not a valid OS" if os == :java
3131
if os == :windows
32-
HOST_OS =~ /(mswin|mingw)/
32+
PLATFORM =~ /(mswin|mingw)/
3333
else
34-
HOST_OS.include?(os.to_s)
34+
PLATFORM.include?(os.to_s)
3535
end
3636
end
3737
end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
class ConstantsLockFile
2+
LOCK_FILE_NAME = '.mspec.constants'
3+
4+
def self.load
5+
if File.exist?(LOCK_FILE_NAME)
6+
File.readlines(LOCK_FILE_NAME).map(&:chomp)
7+
else
8+
[]
9+
end
10+
end
11+
12+
def self.dump(ary)
13+
contents = ary.map(&:to_s).uniq.sort.join("\n") + "\n"
14+
File.write(LOCK_FILE_NAME, contents)
15+
end
16+
end
17+
18+
class ConstantLeakError < StandardError
19+
end
20+
21+
class ConstantsLeakCheckerAction
22+
def initialize(save)
23+
@save = save
24+
@check = !save
25+
@constants_locked = ConstantsLockFile.load
26+
@exclude_patterns = MSpecScript.get(:toplevel_constants_excludes) || []
27+
end
28+
29+
def register
30+
MSpec.register :start, self
31+
MSpec.register :before, self
32+
MSpec.register :after, self
33+
MSpec.register :finish, self
34+
end
35+
36+
def start
37+
@constants_start = constants_now
38+
end
39+
40+
def before(state)
41+
@constants_before = constants_now
42+
end
43+
44+
def after(state)
45+
constants = remove_excludes(constants_now - @constants_before - @constants_locked)
46+
47+
if @check && !constants.empty?
48+
MSpec.protect 'Constants leak check' do
49+
raise ConstantLeakError, "Top level constants leaked: #{constants.join(', ')}"
50+
end
51+
end
52+
end
53+
54+
def finish
55+
constants = remove_excludes(constants_now - @constants_start - @constants_locked)
56+
57+
if @save
58+
ConstantsLockFile.dump(@constants_locked + constants)
59+
end
60+
61+
if @check && !constants.empty?
62+
MSpec.protect 'Global constants leak check' do
63+
raise ConstantLeakError, "Top level constants leaked in the whole test suite: #{constants.join(', ')}"
64+
end
65+
end
66+
end
67+
68+
private
69+
70+
def constants_now
71+
Object.constants.map(&:to_s)
72+
end
73+
74+
def remove_excludes(constants)
75+
constants.reject { |name|
76+
@exclude_patterns.any? { |pattern| pattern === name }
77+
}
78+
end
79+
end

spec/mspec/lib/mspec/runner/actions/leakchecker.rb

Lines changed: 52 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@
2424
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2525
# SUCH DAMAGE.
2626

27+
class LeakError < StandardError
28+
end
29+
2730
class LeakChecker
31+
attr_reader :leaks
32+
2833
def initialize
2934
@fd_info = find_fds
3035
@tempfile_info = find_tempfiles
@@ -34,19 +39,18 @@ def initialize
3439
@encoding_info = find_encodings
3540
end
3641

37-
def check(test_name)
38-
@no_leaks = true
39-
leaks = [
40-
check_fd_leak(test_name),
41-
check_tempfile_leak(test_name),
42-
check_thread_leak(test_name),
43-
check_process_leak(test_name),
44-
check_env(test_name),
45-
check_argv(test_name),
46-
check_encodings(test_name)
47-
]
48-
GC.start if leaks.any?
49-
return leaks.none?
42+
def check(state)
43+
@state = state
44+
@leaks = []
45+
check_fd_leak
46+
check_tempfile_leak
47+
check_thread_leak
48+
check_process_leak
49+
check_env
50+
check_argv
51+
check_encodings
52+
GC.start if !@leaks.empty?
53+
@leaks.empty?
5054
end
5155

5256
private
@@ -66,8 +70,7 @@ def find_fds
6670
end
6771
end
6872

69-
def check_fd_leak(test_name)
70-
leaked = false
73+
def check_fd_leak
7174
live1 = @fd_info
7275
if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero?
7376
m[:close]
@@ -76,12 +79,11 @@ def check_fd_leak(test_name)
7679
fd_closed = live1 - live2
7780
if !fd_closed.empty?
7881
fd_closed.each {|fd|
79-
puts "Closed file descriptor: #{test_name}: #{fd}"
82+
leak "Closed file descriptor: #{fd}"
8083
}
8184
end
8285
fd_leaked = live2 - live1
8386
if !fd_leaked.empty?
84-
leaked = true
8587
h = {}
8688
ObjectSpace.each_object(IO) {|io|
8789
inspect = io.inspect
@@ -105,19 +107,18 @@ def check_fd_leak(test_name)
105107
str << s
106108
}
107109
end
108-
puts "Leaked file descriptor: #{test_name}: #{fd}#{str}"
110+
leak "Leaked file descriptor: #{fd}#{str}"
109111
}
110112
#system("lsof -p #$$") if !fd_leaked.empty?
111113
h.each {|fd, list|
112114
next if list.length <= 1
113115
if 1 < list.count {|io, autoclose, inspect| autoclose }
114116
str = list.map {|io, autoclose, inspect| " #{inspect}" + (autoclose ? "(autoclose)" : "") }.sort.join
115-
puts "Multiple autoclose IO object for a file descriptor:#{str}"
117+
leak "Multiple autoclose IO object for a file descriptor:#{str}"
116118
end
117119
}
118120
end
119121
@fd_info = live2
120-
return leaked
121122
end
122123

123124
def extend_tempfile_counter
@@ -152,22 +153,19 @@ def find_tempfiles(prev_count=-1)
152153
end
153154
end
154155

155-
def check_tempfile_leak(test_name)
156+
def check_tempfile_leak
156157
return false unless defined? Tempfile
157158
count1, initial_tempfiles = @tempfile_info
158159
count2, current_tempfiles = find_tempfiles(count1)
159-
leaked = false
160160
tempfiles_leaked = current_tempfiles - initial_tempfiles
161161
if !tempfiles_leaked.empty?
162-
leaked = true
163162
list = tempfiles_leaked.map {|t| t.inspect }.sort
164163
list.each {|str|
165-
puts "Leaked tempfile: #{test_name}: #{str}"
164+
leak "Leaked tempfile: #{str}"
166165
}
167166
tempfiles_leaked.each {|t| t.close! }
168167
end
169168
@tempfile_info = [count2, initial_tempfiles]
170-
return leaked
171169
end
172170

173171
def find_threads
@@ -176,108 +174,98 @@ def find_threads
176174
}
177175
end
178176

179-
def check_thread_leak(test_name)
177+
def check_thread_leak
180178
live1 = @thread_info
181179
live2 = find_threads
182180
thread_finished = live1 - live2
183-
leaked = false
184181
if !thread_finished.empty?
185182
list = thread_finished.map {|t| t.inspect }.sort
186183
list.each {|str|
187-
puts "Finished thread: #{test_name}: #{str}"
184+
leak "Finished thread: #{str}"
188185
}
189186
end
190187
thread_leaked = live2 - live1
191188
if !thread_leaked.empty?
192-
leaked = true
193189
list = thread_leaked.map {|t| t.inspect }.sort
194190
list.each {|str|
195-
puts "Leaked thread: #{test_name}: #{str}"
191+
leak "Leaked thread: #{str}"
196192
}
197193
end
198194
@thread_info = live2
199-
return leaked
200195
end
201196

202-
def check_process_leak(test_name)
197+
def check_process_leak
203198
subprocesses_leaked = Process.waitall
204199
subprocesses_leaked.each { |pid, status|
205-
puts "Leaked subprocess: #{pid}: #{status}"
200+
leak "Leaked subprocess: #{pid}: #{status}"
206201
}
207-
return !subprocesses_leaked.empty?
208202
end
209203

210204
def find_env
211205
ENV.to_h
212206
end
213207

214-
def check_env(test_name)
208+
def check_env
215209
old_env = @env_info
216210
new_env = find_env
217-
return false if old_env == new_env
211+
return if old_env == new_env
212+
218213
(old_env.keys | new_env.keys).sort.each {|k|
219214
if old_env.has_key?(k)
220215
if new_env.has_key?(k)
221216
if old_env[k] != new_env[k]
222-
puts "Environment variable changed: #{test_name} : #{k.inspect} changed : #{old_env[k].inspect} -> #{new_env[k].inspect}"
217+
leak "Environment variable changed : #{k.inspect} changed : #{old_env[k].inspect} -> #{new_env[k].inspect}"
223218
end
224219
else
225-
puts "Environment variable changed: #{test_name} : #{k.inspect} deleted"
220+
leak "Environment variable changed: #{k.inspect} deleted"
226221
end
227222
else
228223
if new_env.has_key?(k)
229-
puts "Environment variable changed: #{test_name} : #{k.inspect} added"
224+
leak "Environment variable changed: #{k.inspect} added"
230225
else
231226
flunk "unreachable"
232227
end
233228
end
234229
}
235230
@env_info = new_env
236-
return true
237231
end
238232

239233
def find_argv
240234
ARGV.map { |e| e.dup }
241235
end
242236

243-
def check_argv(test_name)
237+
def check_argv
244238
old_argv = @argv_info
245239
new_argv = find_argv
246-
leaked = false
247240
if new_argv != old_argv
248-
puts "ARGV changed: #{test_name} : #{old_argv.inspect} to #{new_argv.inspect}"
241+
leak "ARGV changed: #{old_argv.inspect} to #{new_argv.inspect}"
249242
@argv_info = new_argv
250-
leaked = true
251243
end
252-
return leaked
253244
end
254245

255246
def find_encodings
256247
[Encoding.default_internal, Encoding.default_external]
257248
end
258249

259-
def check_encodings(test_name)
250+
def check_encodings
260251
old_internal, old_external = @encoding_info
261252
new_internal, new_external = find_encodings
262-
leaked = false
263253
if new_internal != old_internal
264-
leaked = true
265-
puts "Encoding.default_internal changed: #{test_name} : #{old_internal.inspect} to #{new_internal.inspect}"
254+
leak "Encoding.default_internal changed: #{old_internal.inspect} to #{new_internal.inspect}"
266255
end
267256
if new_external != old_external
268-
leaked = true
269-
puts "Encoding.default_external changed: #{test_name} : #{old_external.inspect} to #{new_external.inspect}"
257+
leak "Encoding.default_external changed: #{old_external.inspect} to #{new_external.inspect}"
270258
end
271259
@encoding_info = [new_internal, new_external]
272-
return leaked
273260
end
274261

275-
def puts(*args)
276-
if @no_leaks
277-
@no_leaks = false
278-
print "\n"
262+
def leak(message)
263+
if @leaks.empty?
264+
$stderr.puts "\n"
265+
$stderr.puts @state.description
279266
end
280-
super(*args)
267+
@leaks << message
268+
$stderr.puts message
281269
end
282270
end
283271

@@ -292,9 +280,14 @@ def start
292280
end
293281

294282
def after(state)
295-
unless @checker.check(state.description)
283+
unless @checker.check(state)
284+
leak_messages = @checker.leaks
285+
location = state.description
296286
if state.example
297-
puts state.example.source_location.join(':')
287+
location = "#{location}\n#{state.example.source_location.join(':')}"
288+
end
289+
MSpec.protect(location) do
290+
raise LeakError, leak_messages.join("\n")
298291
end
299292
end
300293
end

spec/mspec/lib/mspec/runner/formatters/dotted.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
require 'mspec/expectations/expectations'
22
require 'mspec/runner/actions/timer'
33
require 'mspec/runner/actions/tally'
4-
require 'mspec/runner/actions/leakchecker' if ENV['CHECK_LEAKS']
4+
5+
if ENV['CHECK_LEAKS']
6+
require 'mspec/runner/actions/leakchecker'
7+
require 'mspec/runner/actions/constants_leak_checker'
8+
end
59

610
class DottedFormatter
711
attr_reader :exceptions, :timer, :tally
@@ -25,7 +29,11 @@ def initialize(out=nil)
2529
def register
2630
(@timer = TimerAction.new).register
2731
(@tally = TallyAction.new).register
28-
LeakCheckerAction.new.register if ENV['CHECK_LEAKS']
32+
if ENV['CHECK_LEAKS']
33+
save = ENV['CHECK_LEAKS'] == 'save'
34+
LeakCheckerAction.new.register
35+
ConstantsLeakCheckerAction.new(save).register
36+
end
2937
@counter = @tally.counter
3038

3139
MSpec.register :exception, self

0 commit comments

Comments
 (0)