Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit f121b0f

Browse files
committed
Update shared context to minimize loaded stdlibs.
Fixes #177.
1 parent 732245d commit f121b0f

File tree

4 files changed

+117
-7
lines changed

4 files changed

+117
-7
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
require 'rspec/support/spec/shell_out'
2+
require 'tempfile'
3+
4+
RSpec.shared_examples_for "library wide checks" do |lib, options|
5+
consider_a_test_env_file = options.fetch(:consider_a_test_env_file, /MATCHES NOTHING/)
6+
allowed_loaded_feature_regexps = options.fetch(:allowed_loaded_feature_regexps, [])
7+
preamble_for_lib = options[:preamble_for_lib]
8+
preamble_for_spec = "require 'rspec/core'; require 'spec_helper'"
9+
10+
include RSpec::Support::ShellOut
11+
12+
define_method :files_to_require_for do |sub_dir|
13+
slash = File::SEPARATOR
14+
lib_path_re = /#{slash + lib}[^#{slash}]*#{slash}lib/
15+
load_path = $LOAD_PATH.grep(lib_path_re).first
16+
directory = load_path.sub(/lib$/, sub_dir)
17+
files = Dir["#{directory}/**/*.rb"]
18+
extract_regex = /#{Regexp.escape(directory) + File::SEPARATOR}(.+)\.rb$/
19+
20+
# We sort to ensure the files are loaded in a consistent order, regardless
21+
# of OS. Otherwise, it could load in a different order on Travis than
22+
# locally, and potentially trigger a "circular require considered harmful"
23+
# warning or similar.
24+
files.sort.map { |file| file[extract_regex, 1] }
25+
end
26+
27+
def command_from(code_lines)
28+
code_lines.join("\n")
29+
end
30+
31+
def load_all_files(files, preamble, postamble=nil)
32+
requires = files.map { |f| "require '#{f}'" }
33+
command = command_from(Array(preamble) + requires + Array(postamble))
34+
35+
stdout, stderr, status = with_env 'NO_COVERAGE' => '1' do
36+
options = %w[ -w ]
37+
options << "--disable=gem" if RUBY_VERSION.to_f >= 1.9 && RSpec::Support::Ruby.mri?
38+
run_ruby_with_current_load_path(command, *options)
39+
end
40+
41+
# Ignore bundler warning.
42+
stderr = stderr.split("\n").reject { |l| l =~ %r{bundler/source/rubygems} }.join("\n")
43+
[stdout, stderr, status.exitstatus]
44+
end
45+
46+
define_method :load_all_lib_files do
47+
files = all_lib_files - lib_test_env_files
48+
preamble = ['orig_loaded_features = $".dup', preamble_for_lib]
49+
postamble = [
50+
'loaded_features = ($" - orig_loaded_features).join("\n")',
51+
"File.open('#{loaded_features_tempfile.path}', 'w') { |f| f.write(loaded_features) }"
52+
]
53+
54+
load_all_files(files, preamble, postamble)
55+
end
56+
57+
define_method :load_all_spec_files do
58+
files = files_to_require_for("spec") + lib_test_env_files
59+
load_all_files(files, preamble_for_spec)
60+
end
61+
62+
attr_reader :loaded_features_tempfile, :all_lib_files, :lib_test_env_files,
63+
:lib_file_results, :spec_file_results
64+
65+
before(:context) do
66+
@loaded_features_tempfile = Tempfile.new("loaded_features.txt")
67+
@all_lib_files = files_to_require_for("lib")
68+
@lib_test_env_files = all_lib_files.grep(consider_a_test_env_file)
69+
70+
@lib_file_results, @spec_file_results = [
71+
# Load them in parallel so it's faster...
72+
Thread.new { load_all_lib_files },
73+
Thread.new { load_all_spec_files }
74+
].map(&:join).map(&:value)
75+
end
76+
77+
def have_successful_no_warnings_output
78+
eq ["", "", 0]
79+
end
80+
81+
it "issues no warnings when loaded", :slow do
82+
expect(lib_file_results).to have_successful_no_warnings_output
83+
end
84+
85+
it "issues no warnings when the spec files are loaded", :slow do
86+
expect(spec_file_results).to have_successful_no_warnings_output
87+
end
88+
89+
it 'only loads a known set of stdlibs so gem authors are forced ' \
90+
'to load libs they use to have passing specs', :slow do
91+
loaded_features = File.read(loaded_features_tempfile.path).split("\n")
92+
if RUBY_VERSION == '1.8.7'
93+
# On 1.8.7, $" returns the relative require path if that was used
94+
# to require the file. LIB_REGEX will not match the relative version
95+
# since it has a `/lib` prefix. Here we deal with this by expanding
96+
# relative files relative to the $LOAD_PATH dir (lib).
97+
Dir.chdir("lib") { loaded_features.map! { |f| File.expand_path(f) } }
98+
end
99+
100+
loaded_features.reject! { |feature| RSpec::CallerFilter::LIB_REGEX =~ feature }
101+
loaded_features.reject! { |feature| /diff\/lcs/ =~ feature }
102+
loaded_features.reject! { |feature| allowed_loaded_feature_regexps.any? { |r| r =~ feature } }
103+
104+
expect(loaded_features).to eq([])
105+
end
106+
end

lib/rspec/support/spec/prevent_load_time_warnings.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# This file is deprecated, and is being replaced by library_wide_checks.rb.
2+
# For now we have to keep it until core/expectations/mocks have been updated.
3+
14
require 'rspec/support/spec/shell_out'
25

36
module RSpec

lib/rspec/support/spec/shell_out.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ def shell_out(*command)
2222
return stdout, filter(stderr), status
2323
end
2424
else # 1.8.7
25+
# popen3 doesn't provide the exit status so we fake it out.
26+
FakeProcessStatus = Struct.new(:exitstatus)
27+
2528
def shell_out(*command)
2629
stdout = stderr = nil
2730

@@ -30,8 +33,7 @@ def shell_out(*command)
3033
stderr = err.read
3134
end
3235

33-
# popen3 doesn't provide the exit status so we fake it out.
34-
status = instance_double(Process::Status, :exitstatus => 0)
36+
status = FakeProcessStatus.new(0)
3537
return stdout, filter(stderr), status
3638
end
3739
end

spec/rspec/support_spec.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
require 'rspec/support'
2-
require 'rspec/support/spec/prevent_load_time_warnings'
2+
require 'rspec/support/spec/library_wide_checks'
33

44
module RSpec
55
describe Support do
66
extend Support::RubyFeatures
77

8-
it_behaves_like "a library that issues no warnings when loaded", "rspec-support",
9-
# Define methods that some of our rspec/support/spec files use at load time.
10-
"module RSpec; def self.configure; end; def self.shared_context(*); end; def self.shared_examples_for(*); end; end",
11-
'require "rspec/support"'
8+
it_behaves_like "library wide checks", "rspec-support",
9+
:consider_a_test_env_file => %r{rspec/support/spec},
10+
:allowed_loaded_feature_regexps => [/prettyprint.rb/, /pp.rb/]
1211

1312
describe '.method_handle_for(object, method_name)' do
1413
untampered_class = Class.new do

0 commit comments

Comments
 (0)