Cleanup the rubocop config

Go back to rubocop 0.80 for Ruby 2.3 support.
Stop depending on the company style guide, as it needs a newer
rubocop.
This commit is contained in:
Jean Boussier 2022-01-13 10:05:07 +01:00
parent 12ee08bf86
commit b277e75e85
49 changed files with 790 additions and 800 deletions

View File

@ -1,13 +1,8 @@
inherit_from: .rubocop_todo.yml
inherit_gem:
rubocop-shopify: rubocop.yml
AllCops: AllCops:
Exclude: Exclude:
- 'vendor/**/*' - 'vendor/**/*'
- 'tmp/**/*' - 'tmp/**/*'
TargetRubyVersion: 2.5 TargetRubyVersion: 2.3
# This doesn't take into account retrying from an exception # This doesn't take into account retrying from an exception
Lint/SuppressedException: Lint/SuppressedException:
@ -17,6 +12,149 @@ Lint/SuppressedException:
Style/EmptyLiteral: Style/EmptyLiteral:
Enabled: false Enabled: false
Style/EmptyMethod:
Enabled: false
# allow the use of globals which makes sense in a CLI app like this # allow the use of globals which makes sense in a CLI app like this
Style/GlobalVars: Style/GlobalVars:
Enabled: false Enabled: false
Style/PercentLiteralDelimiters:
Enabled: false
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: comma
Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma
Layout/LineLength:
Max: 120
Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma
Metrics/AbcSize:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/ModuleLength:
Enabled: false
Metrics/ParameterLists:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
Naming/MethodName:
Exclude:
- 'test/**/*'
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
Naming/RescuedExceptionsVariableName:
PreferredName: error
Bundler/OrderedGems:
Enabled: false
Gemspec/OrderedDependencies:
Enabled: false
Gemspec/DuplicatedAssignment:
Enabled: false
Layout/MultilineMethodCallIndentation:
EnforcedStyle: indented
Style/SymbolArray:
Enabled: false
Style/StderrPuts:
Enabled: false
Style/ModuleFunction:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/GuardClause:
Enabled: false
Layout/EndAlignment:
EnforcedStyleAlignWith: start_of_line
Layout/RescueEnsureAlignment:
Enabled: false
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent
Style/NumericPredicate:
Enabled: false
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
Lint/AssignmentInCondition:
AllowSafeAssignment: true
Lint/UnusedMethodArgument:
AllowUnusedKeywordArguments: true
Security/MarshalLoad:
Enabled: false
Security/YAMLLoad:
Enabled: false
Style/Alias:
EnforcedStyle: prefer_alias_method
Style/Documentation:
Enabled: false
Style/DoubleNegation:
Enabled: false
Style/CommentedKeyword:
Enabled: false
Naming/VariableNumber:
Enabled: false
Style/Next:
Enabled: false
Style/StringLiterals:
EnforcedStyle: double_quotes
Lint/RaiseException:
Enabled: true
Lint/StructNewOverride:
Enabled: true
Style/HashEachMethods:
Enabled: true
Style/HashTransformKeys:
Enabled: true
Style/HashTransformValues:
Enabled: true

View File

@ -1,213 +0,0 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2022-01-12 21:26:07 UTC using RuboCop version 1.24.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Configuration parameters: Include.
# Include: **/*.gemspec
Gemspec/DuplicatedAssignment:
Exclude:
- 'bootsnap.gemspec'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemspec
Gemspec/OrderedDependencies:
Exclude:
- 'bootsnap.gemspec'
# Offense count: 37
# Cop supports --auto-correct.
Layout/EmptyLineAfterMagicComment:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
Layout/EmptyLines:
Exclude:
- 'lib/bootsnap.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
Layout/ExtraSpacing:
Exclude:
- 'lib/bootsnap/cli.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented
Layout/LineEndStringConcatenationIndentation:
Exclude:
- 'lib/bootsnap/compile_cache.rb'
- 'test/load_path_cache/realpath_cache_test.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented
Layout/MultilineOperationIndentation:
Exclude:
- 'lib/bootsnap/compile_cache.rb'
- 'lib/bootsnap/load_path_cache.rb'
# Offense count: 1
# Cop supports --auto-correct.
Layout/RescueEnsureAlignment:
Exclude:
- 'lib/bootsnap/load_path_cache/path.rb'
# Offense count: 12
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
# SupportedStyles: space, no_space, compact
# SupportedStylesForEmptyBraces: space, no_space
Layout/SpaceInsideHashLiteralBraces:
Exclude:
- 'test/compile_cache/json_test.rb'
- 'test/compile_cache/yaml_test.rb'
# Offense count: 5
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Exclude:
- 'lib/bootsnap/cli.rb'
- 'lib/bootsnap/compile_cache/json.rb'
- 'lib/bootsnap/compile_cache/yaml.rb'
- 'lib/bootsnap/load_path_cache/loaded_features_index.rb'
# Offense count: 5
# Configuration parameters: IgnoredPatterns.
# SupportedStyles: snake_case, camelCase
Naming/MethodName:
EnforcedStyle: snake_case
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Exclude:
- 'lib/bootsnap.rb'
# Offense count: 36
# Cop supports --auto-correct.
# Configuration parameters: IgnoreMacros, IgnoredMethods, IgnoredPatterns, IncludedMacros, AllowParenthesesInMultilineCall, AllowParenthesesInChaining, AllowParenthesesInCamelCaseMethod, AllowParenthesesInStringInterpolation, EnforcedStyle.
# SupportedStyles: require_parentheses, omit_parentheses
Style/MethodCallWithArgsParentheses:
Exclude:
- 'lib/bootsnap.rb'
- 'lib/bootsnap/cli.rb'
- 'test/cli_test.rb'
- 'test/compile_cache/json_test.rb'
- 'test/compile_cache/yaml_test.rb'
- 'test/compile_cache_test.rb'
- 'test/load_path_cache/cache_test.rb'
- 'test/load_path_cache/core_ext/kernel_require_test.rb'
- 'test/load_path_cache/store_test.rb'
- 'test/worker_pool_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: AllowedMethods.
# AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with
Style/NestedParenthesizedCalls:
Exclude:
- 'test/load_path_cache/core_ext/kernel_require_test.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength.
# SupportedStyles: skip_modifier_ifs, always
Style/Next:
Exclude:
- 'lib/bootsnap/cli.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/RedundantBegin:
Exclude:
- 'lib/bootsnap/load_path_cache/path.rb'
- 'lib/bootsnap/load_path_cache/store.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantFreeze:
Exclude:
- 'lib/bootsnap/load_path_cache/store.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantParentheses:
Exclude:
- 'lib/bootsnap/load_path_cache/cache.rb'
# Offense count: 7
# Cop supports --auto-correct.
Style/RedundantSelf:
Exclude:
- 'lib/bootsnap/cli.rb'
- 'lib/bootsnap/compile_cache/json.rb'
- 'lib/bootsnap/compile_cache/yaml.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods.
# AllowedMethods: present?, blank?, presence, try, try!
Style/SafeNavigation:
Exclude:
- 'lib/bootsnap/compile_cache/json.rb'
- 'lib/bootsnap/compile_cache/yaml.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: RequireEnglish, EnforcedStyle.
# SupportedStyles: use_perl_names, use_english_names
Style/SpecialGlobalVars:
Exclude:
- 'test/worker_pool_test.rb'
# Offense count: 593
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
Enabled: false
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiteralsInInterpolation:
Exclude:
- 'test/load_path_cache/path_test.rb'
- 'test/load_path_cache/realpath_cache_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: .
# SupportedStyles: percent, brackets
Style/SymbolArray:
EnforcedStyle: percent
MinSize: 3
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInHashLiteral:
Exclude:
- 'bootsnap.gemspec'
# Offense count: 15
# Cop supports --auto-correct.
# Configuration parameters: MinSize, WordRegex.
# SupportedStyles: percent, brackets
Style/WordArray:
EnforcedStyle: percent

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org'
source "https://rubygems.org"
# Specify your gem's dependencies in bootsnap.gemspec # Specify your gem's dependencies in bootsnap.gemspec
gemspec gemspec
@ -9,7 +10,7 @@ if ENV["PSYCH_4"]
end end
group :development do group :development do
gem 'rubocop' gem "rubocop", "0.81.0" # Ruby 2.3 support
gem 'rubocop-shopify', require: false gem "rubocop-shopify", require: false
gem 'byebug', platform: :ruby gem "byebug", platform: :ruby
end end

View File

@ -1,12 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require('rake/extensiontask')
require('bundler/gem_tasks')
gemspec = Gem::Specification.load('bootsnap.gemspec') require("rake/extensiontask")
require("bundler/gem_tasks")
gemspec = Gem::Specification.load("bootsnap.gemspec")
Rake::ExtensionTask.new do |ext| Rake::ExtensionTask.new do |ext|
ext.name = 'bootsnap' ext.name = "bootsnap"
ext.ext_dir = 'ext/bootsnap' ext.ext_dir = "ext/bootsnap"
ext.lib_dir = 'lib/bootsnap' ext.lib_dir = "lib/bootsnap"
ext.gem_spec = gemspec ext.gem_spec = gemspec
end end

View File

@ -1,8 +1,8 @@
# coding: utf-8
# frozen_string_literal: true # frozen_string_literal: true
lib = File.expand_path('../lib', __FILE__)
lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require('bootsnap/version') require("bootsnap/version")
Gem::Specification.new do |spec| Gem::Specification.new do |spec|
spec.name = "bootsnap" spec.name = "bootsnap"
@ -17,30 +17,30 @@ Gem::Specification.new do |spec|
spec.homepage = "https://github.com/Shopify/bootsnap" spec.homepage = "https://github.com/Shopify/bootsnap"
spec.metadata = { spec.metadata = {
'bug_tracker_uri' => 'https://github.com/Shopify/bootsnap/issues', "bug_tracker_uri" => "https://github.com/Shopify/bootsnap/issues",
'changelog_uri' => 'https://github.com/Shopify/bootsnap/blob/main/CHANGELOG.md', "changelog_uri" => "https://github.com/Shopify/bootsnap/blob/main/CHANGELOG.md",
'source_code_uri' => 'https://github.com/Shopify/bootsnap', "source_code_uri" => "https://github.com/Shopify/bootsnap",
'allowed_push_host' => 'https://rubygems.org' "allowed_push_host" => "https://rubygems.org",
} }
spec.files = %x(git ls-files -z ext lib).split("\x0") + %w(CHANGELOG.md LICENSE.txt README.md) spec.files = `git ls-files -z ext lib`.split("\x0") + %w(CHANGELOG.md LICENSE.txt README.md)
spec.require_paths = %w(lib) spec.require_paths = %w(lib)
spec.bindir = 'exe' spec.bindir = "exe"
spec.executables = %w(bootsnap) spec.executables = %w(bootsnap)
spec.required_ruby_version = '>= 2.3.0' spec.required_ruby_version = ">= 2.3.0"
if RUBY_PLATFORM =~ /java/ if RUBY_PLATFORM =~ /java/
spec.platform = 'java' spec.platform = "java"
else else
spec.platform = Gem::Platform::RUBY spec.platform = Gem::Platform::RUBY
spec.extensions = ['ext/bootsnap/extconf.rb'] spec.extensions = ["ext/bootsnap/extconf.rb"]
end end
spec.add_development_dependency("bundler") spec.add_development_dependency("bundler")
spec.add_development_dependency('rake') spec.add_development_dependency("rake")
spec.add_development_dependency('rake-compiler') spec.add_development_dependency("rake-compiler")
spec.add_development_dependency("minitest", "~> 5.0") spec.add_development_dependency("minitest", "~> 5.0")
spec.add_development_dependency("mocha", "~> 1.2") spec.add_development_dependency("mocha", "~> 1.2")

View File

@ -1,5 +1,5 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
require 'bootsnap/cli' require "bootsnap/cli"
exit Bootsnap::CLI.new(ARGV).run exit Bootsnap::CLI.new(ARGV).run

View File

@ -1,22 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
require("mkmf") require("mkmf")
if RUBY_ENGINE == 'ruby' if RUBY_ENGINE == "ruby"
$CFLAGS << ' -O3 ' $CFLAGS << " -O3 "
$CFLAGS << ' -std=c99' $CFLAGS << " -std=c99"
# ruby.h has some -Wpedantic fails in some cases # ruby.h has some -Wpedantic fails in some cases
# (e.g. https://github.com/Shopify/bootsnap/issues/15) # (e.g. https://github.com/Shopify/bootsnap/issues/15)
unless ['0', '', nil].include?(ENV['BOOTSNAP_PEDANTIC']) unless ["0", "", nil].include?(ENV["BOOTSNAP_PEDANTIC"])
$CFLAGS << ' -Wall' $CFLAGS << " -Wall"
$CFLAGS << ' -Werror' $CFLAGS << " -Werror"
$CFLAGS << ' -Wextra' $CFLAGS << " -Wextra"
$CFLAGS << ' -Wpedantic' $CFLAGS << " -Wpedantic"
$CFLAGS << ' -Wno-unused-parameter' # VALUE self has to be there but we don't care what it is. $CFLAGS << " -Wno-unused-parameter" # VALUE self has to be there but we don't care what it is.
$CFLAGS << ' -Wno-keyword-macro' # hiding return $CFLAGS << " -Wno-keyword-macro" # hiding return
$CFLAGS << ' -Wno-gcc-compat' # ruby.h 2.6.0 on macos 10.14, dunno $CFLAGS << " -Wno-gcc-compat" # ruby.h 2.6.0 on macos 10.14, dunno
$CFLAGS << ' -Wno-compound-token-split-by-macro' $CFLAGS << " -Wno-compound-token-split-by-macro"
end end
create_makefile("bootsnap/bootsnap") create_makefile("bootsnap/bootsnap")

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative('bootsnap/version') require_relative("bootsnap/version")
require_relative('bootsnap/bundler') require_relative("bootsnap/bundler")
require_relative('bootsnap/load_path_cache') require_relative("bootsnap/load_path_cache")
require_relative('bootsnap/compile_cache') require_relative("bootsnap/compile_cache")
module Bootsnap module Bootsnap
InvalidConfiguration = Class.new(StandardError) InvalidConfiguration = Class.new(StandardError)
@ -18,10 +18,10 @@ module Bootsnap
def self.logger=(logger) def self.logger=(logger)
@logger = logger @logger = logger
if logger.respond_to?(:debug) self.instrumentation = if logger.respond_to?(:debug)
self.instrumentation = ->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") } ->(event, path) { @logger.debug("[Bootsnap] #{event} #{path}") }
else else
self.instrumentation = ->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") } ->(event, path) { @logger.call("[Bootsnap] #{event} #{path}") }
end end
end end
@ -62,13 +62,15 @@ module Bootsnap
"to turn `compile_cache_iseq` off on Ruby 2.5" "to turn `compile_cache_iseq` off on Ruby 2.5"
end end
Bootsnap::LoadPathCache.setup( if load_path_cache
cache_path: cache_dir + '/bootsnap/load-path-cache', Bootsnap::LoadPathCache.setup(
development_mode: development_mode, cache_path: cache_dir + "/bootsnap/load-path-cache",
) if load_path_cache development_mode: development_mode,
)
end
Bootsnap::CompileCache.setup( Bootsnap::CompileCache.setup(
cache_dir: cache_dir + '/bootsnap/compile-cache', cache_dir: cache_dir + "/bootsnap/compile-cache",
iseq: compile_cache_iseq, iseq: compile_cache_iseq,
yaml: compile_cache_yaml, yaml: compile_cache_yaml,
json: compile_cache_json, json: compile_cache_json,
@ -79,18 +81,18 @@ module Bootsnap
return @iseq_cache_supported if defined? @iseq_cache_supported return @iseq_cache_supported if defined? @iseq_cache_supported
ruby_version = Gem::Version.new(RUBY_VERSION) ruby_version = Gem::Version.new(RUBY_VERSION)
@iseq_cache_supported = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.6.0') @iseq_cache_supported = ruby_version < Gem::Version.new("2.5.0") || ruby_version >= Gem::Version.new("2.6.0")
end end
def self.default_setup def self.default_setup
env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['ENV'] env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
development_mode = ['', nil, 'development'].include?(env) development_mode = ["", nil, "development"].include?(env)
unless ENV['DISABLE_BOOTSNAP'] unless ENV["DISABLE_BOOTSNAP"]
cache_dir = ENV['BOOTSNAP_CACHE_DIR'] cache_dir = ENV["BOOTSNAP_CACHE_DIR"]
unless cache_dir unless cache_dir
config_dir_frame = caller.detect do |line| config_dir_frame = caller.detect do |line|
line.include?('/config/') line.include?("/config/")
end end
unless config_dir_frame unless config_dir_frame
@ -102,35 +104,34 @@ module Bootsnap
end end
path = config_dir_frame.split(/:\d+:/).first path = config_dir_frame.split(/:\d+:/).first
path = File.dirname(path) until File.basename(path) == 'config' path = File.dirname(path) until File.basename(path) == "config"
app_root = File.dirname(path) app_root = File.dirname(path)
cache_dir = File.join(app_root, 'tmp', 'cache') cache_dir = File.join(app_root, "tmp", "cache")
end end
setup( setup(
cache_dir: cache_dir, cache_dir: cache_dir,
development_mode: development_mode, development_mode: development_mode,
load_path_cache: !ENV['DISABLE_BOOTSNAP_LOAD_PATH_CACHE'], load_path_cache: !ENV["DISABLE_BOOTSNAP_LOAD_PATH_CACHE"],
compile_cache_iseq: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'] && iseq_cache_supported?, compile_cache_iseq: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"] && iseq_cache_supported?,
compile_cache_yaml: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'], compile_cache_yaml: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
compile_cache_json: !ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'], compile_cache_json: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
) )
if ENV['BOOTSNAP_LOG'] if ENV["BOOTSNAP_LOG"]
log! log!
end end
end end
end end
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
def self.absolute_path?(path) def self.absolute_path?(path)
path[1] == ':' path[1] == ":"
end end
else else
def self.absolute_path?(path) def self.absolute_path?(path)
path.start_with?('/') path.start_with?("/")
end end
end end
end end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Bootsnap module Bootsnap
extend(self) extend(self)

View File

@ -1,10 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'bootsnap' require "bootsnap"
require 'bootsnap/cli/worker_pool' require "bootsnap/cli/worker_pool"
require 'optparse' require "optparse"
require 'fileutils' require "fileutils"
require 'etc' require "etc"
module Bootsnap module Bootsnap
class CLI class CLI
@ -25,7 +25,7 @@ module Bootsnap
def initialize(argv) def initialize(argv)
@argv = argv @argv = argv
self.cache_dir = ENV.fetch('BOOTSNAP_CACHE_DIR', 'tmp/cache') self.cache_dir = ENV.fetch("BOOTSNAP_CACHE_DIR", "tmp/cache")
self.compile_gemfile = false self.compile_gemfile = false
self.exclude = nil self.exclude = nil
self.verbose = false self.verbose = false
@ -36,16 +36,16 @@ module Bootsnap
end end
def precompile_command(*sources) def precompile_command(*sources)
require 'bootsnap/compile_cache/iseq' require "bootsnap/compile_cache/iseq"
require 'bootsnap/compile_cache/yaml' require "bootsnap/compile_cache/yaml"
require 'bootsnap/compile_cache/json' require "bootsnap/compile_cache/json"
fix_default_encoding do fix_default_encoding do
Bootsnap::CompileCache::ISeq.cache_dir = self.cache_dir Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
Bootsnap::CompileCache::YAML.init! Bootsnap::CompileCache::YAML.init!
Bootsnap::CompileCache::YAML.cache_dir = self.cache_dir Bootsnap::CompileCache::YAML.cache_dir = cache_dir
Bootsnap::CompileCache::JSON.init! Bootsnap::CompileCache::JSON.init!
Bootsnap::CompileCache::JSON.cache_dir = self.cache_dir Bootsnap::CompileCache::JSON.cache_dir = cache_dir
@work_pool = WorkerPool.create(size: jobs, jobs: { @work_pool = WorkerPool.create(size: jobs, jobs: {
ruby: method(:precompile_ruby), ruby: method(:precompile_ruby),
@ -61,7 +61,7 @@ module Bootsnap
if compile_gemfile if compile_gemfile
# Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling. # Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
gem_exclude = Regexp.union([exclude, '/spec/', '/test/'].compact) gem_exclude = Regexp.union([exclude, "/spec/", "/test/"].compact)
precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude) precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude)
# Gems that include JSON or YAML files usually don't put them in `lib/`. # Gems that include JSON or YAML files usually don't put them in `lib/`.
@ -72,7 +72,7 @@ module Bootsnap
precompile_json_files(gem_paths, exclude: gem_exclude) precompile_json_files(gem_paths, exclude: gem_exclude)
end end
if exitstatus = @work_pool.shutdown if (exitstatus = @work_pool.shutdown)
exit(exitstatus) exit(exitstatus)
end end
end end
@ -89,7 +89,7 @@ module Bootsnap
if dir_sort if dir_sort
def list_files(path, pattern) def list_files(path, pattern)
if File.directory?(path) if File.directory?(path)
Dir[File.join(path, pattern), sort: false] Dir[File.join(path, pattern), sort: false]
elsif File.exist?(path) elsif File.exist?(path)
[path] [path]
else else
@ -99,7 +99,7 @@ module Bootsnap
else else
def list_files(path, pattern) def list_files(path, pattern)
if File.directory?(path) if File.directory?(path)
Dir[File.join(path, pattern)] Dir[File.join(path, pattern)]
elsif File.exist?(path) elsif File.exist?(path)
[path] [path]
else else
@ -126,9 +126,9 @@ module Bootsnap
load_paths.each do |path| load_paths.each do |path|
if !exclude || !exclude.match?(path) if !exclude || !exclude.match?(path)
list_files(path, '**/*.{yml,yaml}').each do |yaml_file| list_files(path, "**/*.{yml,yaml}").each do |yaml_file|
# We ignore hidden files to not match the various .ci.yml files # We ignore hidden files to not match the various .ci.yml files
if !File.basename(yaml_file).start_with?('.') && (!exclude || !exclude.match?(yaml_file)) if !File.basename(yaml_file).start_with?(".") && (!exclude || !exclude.match?(yaml_file))
@work_pool.push(:yaml, yaml_file) @work_pool.push(:yaml, yaml_file)
end end
end end
@ -149,9 +149,9 @@ module Bootsnap
load_paths.each do |path| load_paths.each do |path|
if !exclude || !exclude.match?(path) if !exclude || !exclude.match?(path)
list_files(path, '**/*.json').each do |json_file| list_files(path, "**/*.json").each do |json_file|
# We ignore hidden files to not match the various .config.json files # We ignore hidden files to not match the various .config.json files
if !File.basename(json_file).start_with?('.') && (!exclude || !exclude.match?(json_file)) if !File.basename(json_file).start_with?(".") && (!exclude || !exclude.match?(json_file))
@work_pool.push(:json, json_file) @work_pool.push(:json, json_file)
end end
end end
@ -172,7 +172,7 @@ module Bootsnap
load_paths.each do |path| load_paths.each do |path|
if !exclude || !exclude.match?(path) if !exclude || !exclude.match?(path)
list_files(path, '**/*.rb').each do |ruby_file| list_files(path, "**/*.rb").each do |ruby_file|
if !exclude || !exclude.match?(ruby_file) if !exclude || !exclude.match?(ruby_file)
@work_pool.push(:ruby, ruby_file) @work_pool.push(:ruby, ruby_file)
end end
@ -210,7 +210,7 @@ module Bootsnap
end end
def cache_dir=(dir) def cache_dir=(dir)
@cache_dir = File.expand_path(File.join(dir, 'bootsnap/compile-cache')) @cache_dir = File.expand_path(File.join(dir, "bootsnap/compile-cache"))
end end
def exclude_pattern(pattern) def exclude_pattern(pattern)
@ -225,24 +225,24 @@ module Bootsnap
opts.separator "GLOBAL OPTIONS" opts.separator "GLOBAL OPTIONS"
opts.separator "" opts.separator ""
help = <<~EOS help = <<~HELP
Path to the bootsnap cache directory. Defaults to tmp/cache Path to the bootsnap cache directory. Defaults to tmp/cache
EOS HELP
opts.on('--cache-dir DIR', help.strip) do |dir| opts.on("--cache-dir DIR", help.strip) do |dir|
self.cache_dir = dir self.cache_dir = dir
end end
help = <<~EOS help = <<~HELP
Print precompiled paths. Print precompiled paths.
EOS HELP
opts.on('--verbose', '-v', help.strip) do opts.on("--verbose", "-v", help.strip) do
self.verbose = true self.verbose = true
end end
help = <<~EOS help = <<~HELP
Number of workers to use. Default to number of processors, set to 0 to disable multi-processing. Number of workers to use. Default to number of processors, set to 0 to disable multi-processing.
EOS HELP
opts.on('--jobs JOBS', '-j', help.strip) do |jobs| opts.on("--jobs JOBS", "-j", help.strip) do |jobs|
self.jobs = Integer(jobs) self.jobs = Integer(jobs)
end end
@ -251,30 +251,30 @@ module Bootsnap
opts.separator "" opts.separator ""
opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories" opts.separator " precompile [DIRECTORIES...]: Precompile all .rb files in the passed directories"
help = <<~EOS help = <<~HELP
Precompile the gems in Gemfile Precompile the gems in Gemfile
EOS HELP
opts.on('--gemfile', help) { self.compile_gemfile = true } opts.on("--gemfile", help) { self.compile_gemfile = true }
help = <<~EOS help = <<~HELP
Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api' Path pattern to not precompile. e.g. --exclude 'aws-sdk|google-api'
EOS HELP
opts.on('--exclude PATTERN', help) { |pattern| exclude_pattern(pattern) } opts.on("--exclude PATTERN", help) { |pattern| exclude_pattern(pattern) }
help = <<~EOS help = <<~HELP
Disable ISeq (.rb) precompilation. Disable ISeq (.rb) precompilation.
EOS HELP
opts.on('--no-iseq', help) { self.iseq = false } opts.on("--no-iseq", help) { self.iseq = false }
help = <<~EOS help = <<~HELP
Disable YAML precompilation. Disable YAML precompilation.
EOS HELP
opts.on('--no-yaml', help) { self.yaml = false } opts.on("--no-yaml", help) { self.yaml = false }
help = <<~EOS help = <<~HELP
Disable JSON precompilation. Disable JSON precompilation.
EOS HELP
opts.on('--no-json', help) { self.json = false } opts.on("--no-json", help) { self.json = false }
end end
end end
end end

View File

@ -63,6 +63,7 @@ module Bootsnap
loop do loop do
job, *args = Marshal.load(@pipe_out) job, *args = Marshal.load(@pipe_out)
return if job == :exit return if job == :exit
@jobs.fetch(job).call(*args) @jobs.fetch(job).call(*args)
end end
rescue IOError rescue IOError

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Bootsnap module Bootsnap
module CompileCache module CompileCache
Error = Class.new(StandardError) Error = Class.new(StandardError)
@ -7,7 +8,7 @@ module Bootsnap
def self.setup(cache_dir:, iseq:, yaml:, json:) def self.setup(cache_dir:, iseq:, yaml:, json:)
if iseq if iseq
if supported? if supported?
require_relative('compile_cache/iseq') require_relative("compile_cache/iseq")
Bootsnap::CompileCache::ISeq.install!(cache_dir) Bootsnap::CompileCache::ISeq.install!(cache_dir)
elsif $VERBOSE elsif $VERBOSE
warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby") warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
@ -16,7 +17,7 @@ module Bootsnap
if yaml if yaml
if supported? if supported?
require_relative('compile_cache/yaml') require_relative("compile_cache/yaml")
Bootsnap::CompileCache::YAML.install!(cache_dir) Bootsnap::CompileCache::YAML.install!(cache_dir)
elsif $VERBOSE elsif $VERBOSE
warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby") warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
@ -25,7 +26,7 @@ module Bootsnap
if json if json
if supported? if supported?
require_relative('compile_cache/json') require_relative("compile_cache/json")
Bootsnap::CompileCache::JSON.install!(cache_dir) Bootsnap::CompileCache::JSON.install!(cache_dir)
elsif $VERBOSE elsif $VERBOSE
warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby") warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
@ -44,9 +45,9 @@ module Bootsnap
def self.supported? def self.supported?
# only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0 # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
RUBY_ENGINE == 'ruby' && RUBY_ENGINE == "ruby" &&
RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ && RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0") Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
end end
end end
end end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require('bootsnap/bootsnap')
require('zlib') require("bootsnap/bootsnap")
require("zlib")
module Bootsnap module Bootsnap
module CompileCache module CompileCache
@ -23,27 +24,27 @@ module Bootsnap
iseq = begin iseq = begin
RubyVM::InstructionSequence.compile_file(path) RubyVM::InstructionSequence.compile_file(path)
rescue SyntaxError rescue SyntaxError
raise(Uncompilable, 'syntax error') raise(Uncompilable, "syntax error")
end end
begin begin
iseq.to_binary iseq.to_binary
rescue TypeError rescue TypeError
raise(Uncompilable, 'ruby bug #18250') raise(Uncompilable, "ruby bug #18250")
end end
end end
else else
def self.input_to_storage(_, path) def self.input_to_storage(_, path)
RubyVM::InstructionSequence.compile_file(path).to_binary RubyVM::InstructionSequence.compile_file(path).to_binary
rescue SyntaxError rescue SyntaxError
raise(Uncompilable, 'syntax error') raise(Uncompilable, "syntax error")
end end
end end
def self.storage_to_output(binary, _args) def self.storage_to_output(binary, _args)
RubyVM::InstructionSequence.load_from_binary(binary) RubyVM::InstructionSequence.load_from_binary(binary)
rescue RuntimeError => e rescue RuntimeError => error
if e.message == 'broken binary format' if error.message == "broken binary format"
STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary") STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
nil nil
else else
@ -80,8 +81,8 @@ module Bootsnap
Bootsnap::CompileCache::ISeq.fetch(path.to_s) Bootsnap::CompileCache::ISeq.fetch(path.to_s)
rescue Errno::EACCES rescue Errno::EACCES
Bootsnap::CompileCache.permission_error(path) Bootsnap::CompileCache.permission_error(path)
rescue RuntimeError => e rescue RuntimeError => error
if e.message =~ /unmatched platform/ if error.message =~ /unmatched platform/
puts("unmatched platform for file #{path}") puts("unmatched platform for file #{path}")
end end
raise raise

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('bootsnap/bootsnap')
require("bootsnap/bootsnap")
module Bootsnap module Bootsnap
module CompileCache module CompileCache
@ -13,7 +14,7 @@ module Bootsnap
end end
def storage_to_output(data, kwargs) def storage_to_output(data, kwargs)
if kwargs && kwargs.key?(:symbolize_names) if kwargs&.key?(:symbolize_names)
kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names) kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
end end
msgpack_factory.load(data, kwargs) msgpack_factory.load(data, kwargs)
@ -40,22 +41,23 @@ module Bootsnap
end end
def init! def init!
require('json') require("json")
require('msgpack') require("msgpack")
self.msgpack_factory = MessagePack::Factory.new self.msgpack_factory = MessagePack::Factory.new
self.supported_options = [:symbolize_names] self.supported_options = [:symbolize_names]
if ::JSON.parse('["foo"]', freeze: true).first.frozen? if ::JSON.parse('["foo"]', freeze: true).first.frozen?
self.supported_options = [:freeze] self.supported_options = [:freeze]
end end
self.supported_options.freeze supported_options.freeze
end end
end end
module Patch module Patch
def load_file(path, *args) def load_file(path, *args)
return super if args.size > 1 return super if args.size > 1
if kwargs = args.first
if (kwargs = args.first)
return super unless kwargs.is_a?(Hash) return super unless kwargs.is_a?(Hash)
return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty? return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('bootsnap/bootsnap')
require("bootsnap/bootsnap")
module Bootsnap module Bootsnap
module CompileCache module CompileCache
@ -16,7 +17,7 @@ module Bootsnap
end end
def storage_to_output(data, kwargs) def storage_to_output(data, kwargs)
if kwargs && kwargs.key?(:symbolize_names) if kwargs&.key?(:symbolize_names)
kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names) kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
end end
msgpack_factory.load(data, kwargs) msgpack_factory.load(data, kwargs)
@ -33,6 +34,7 @@ module Bootsnap
def strict_load(payload, *args) def strict_load(payload, *args)
ast = ::YAML.parse(payload) ast = ::YAML.parse(payload)
return ast unless ast return ast unless ast
strict_visitor.create(*args).visit(ast) strict_visitor.create(*args).visit(ast)
end end
ruby2_keywords :strict_load if respond_to?(:ruby2_keywords, true) ruby2_keywords :strict_load if respond_to?(:ruby2_keywords, true)
@ -52,14 +54,14 @@ module Bootsnap
end end
def init! def init!
require('yaml') require("yaml")
require('msgpack') require("msgpack")
require('date') require("date")
if Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file) if Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
Patch.send(:remove_method, :unsafe_load_file) Patch.send(:remove_method, :unsafe_load_file)
end end
if Patch.method_defined?(:load_file) && ::YAML::VERSION >= '4' if Patch.method_defined?(:load_file) && ::YAML::VERSION >= "4"
Patch.send(:remove_method, :load_file) Patch.send(:remove_method, :load_file)
end end
@ -74,7 +76,7 @@ module Bootsnap
MessagePack::Timestamp::TYPE, # or just -1 MessagePack::Timestamp::TYPE, # or just -1
Time, Time,
packer: MessagePack::Time::Packer, packer: MessagePack::Time::Packer,
unpacker: MessagePack::Time::Unpacker unpacker: MessagePack::Time::Unpacker,
) )
marshal_fallback = { marshal_fallback = {
@ -94,14 +96,14 @@ module Bootsnap
self.supported_options = [] self.supported_options = []
params = ::YAML.method(:load).parameters params = ::YAML.method(:load).parameters
if params.include?([:key, :symbolize_names]) if params.include?([:key, :symbolize_names])
self.supported_options << :symbolize_names supported_options << :symbolize_names
end end
if params.include?([:key, :freeze]) if params.include?([:key, :freeze])
if factory.load(factory.dump('yaml'), freeze: true).frozen? if factory.load(factory.dump("yaml"), freeze: true).frozen?
self.supported_options << :freeze supported_options << :freeze
end end
end end
self.supported_options.freeze supported_options.freeze
end end
def strict_visitor def strict_visitor
@ -110,6 +112,7 @@ module Bootsnap
if target.tag if target.tag
raise Uncompilable, "YAML tags are not supported: #{target.tag}" raise Uncompilable, "YAML tags are not supported: #{target.tag}"
end end
super super
end end
end end
@ -119,7 +122,8 @@ module Bootsnap
module Patch module Patch
def load_file(path, *args) def load_file(path, *args)
return super if args.size > 1 return super if args.size > 1
if kwargs = args.first
if (kwargs = args.first)
return super unless kwargs.is_a?(Hash) return super unless kwargs.is_a?(Hash)
return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty? return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
end end
@ -140,7 +144,8 @@ module Bootsnap
def unsafe_load_file(path, *args) def unsafe_load_file(path, *args)
return super if args.size > 1 return super if args.size > 1
if kwargs = args.first
if (kwargs = args.first)
return super unless kwargs.is_a?(Hash) return super unless kwargs.is_a?(Hash)
return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty? return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
end end

View File

@ -1,9 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
module Bootsnap module Bootsnap
module ExplicitRequire module ExplicitRequire
ARCHDIR = RbConfig::CONFIG['archdir'] ARCHDIR = RbConfig::CONFIG["archdir"]
RUBYLIBDIR = RbConfig::CONFIG['rubylibdir'] RUBYLIBDIR = RbConfig::CONFIG["rubylibdir"]
DLEXT = RbConfig::CONFIG['DLEXT'] DLEXT = RbConfig::CONFIG["DLEXT"]
def self.from_self(feature) def self.from_self(feature)
require_relative("../#{feature}") require_relative("../#{feature}")

View File

@ -5,9 +5,9 @@ module Bootsnap
ReturnFalse = Class.new(StandardError) ReturnFalse = Class.new(StandardError)
FallbackScan = Class.new(StandardError) FallbackScan = Class.new(StandardError)
DOT_RB = '.rb' DOT_RB = ".rb"
DOT_SO = '.so' DOT_SO = ".so"
SLASH = '/' SLASH = "/"
# If a NameError happens several levels deep, don't re-handle it # If a NameError happens several levels deep, don't re-handle it
# all the way up the chain: mark it once and bubble it up without # all the way up the chain: mark it once and bubble it up without
@ -15,7 +15,7 @@ module Bootsnap
ERROR_TAG_IVAR = :@__bootsnap_rescued ERROR_TAG_IVAR = :@__bootsnap_rescued
DL_EXTENSIONS = ::RbConfig::CONFIG DL_EXTENSIONS = ::RbConfig::CONFIG
.values_at('DLEXT', 'DLEXT2') .values_at("DLEXT", "DLEXT2")
.reject { |ext| !ext || ext.empty? } .reject { |ext| !ext || ext.empty? }
.map { |ext| ".#{ext}" } .map { |ext| ".#{ext}" }
.freeze .freeze
@ -42,24 +42,24 @@ module Bootsnap
@realpath_cache = RealpathCache.new @realpath_cache = RealpathCache.new
@load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode) @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
require_relative('load_path_cache/core_ext/kernel_require') require_relative("load_path_cache/core_ext/kernel_require")
require_relative('load_path_cache/core_ext/loaded_features') require_relative("load_path_cache/core_ext/loaded_features")
end end
def supported? def supported?
RUBY_ENGINE == 'ruby' && RUBY_ENGINE == "ruby" &&
RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
end end
end end
end end
end end
if Bootsnap::LoadPathCache.supported? if Bootsnap::LoadPathCache.supported?
require_relative('load_path_cache/path_scanner') require_relative("load_path_cache/path_scanner")
require_relative('load_path_cache/path') require_relative("load_path_cache/path")
require_relative('load_path_cache/cache') require_relative("load_path_cache/cache")
require_relative('load_path_cache/store') require_relative("load_path_cache/store")
require_relative('load_path_cache/change_observer') require_relative("load_path_cache/change_observer")
require_relative('load_path_cache/loaded_features_index') require_relative("load_path_cache/loaded_features_index")
require_relative('load_path_cache/realpath_cache') require_relative("load_path_cache/realpath_cache")
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative('../explicit_require') require_relative("../explicit_require")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -28,15 +28,16 @@ module Bootsnap
BUILTIN_FEATURES = $LOADED_FEATURES.each_with_object({}) do |feat, features| BUILTIN_FEATURES = $LOADED_FEATURES.each_with_object({}) do |feat, features|
# Builtin features are of the form 'enumerator.so'. # Builtin features are of the form 'enumerator.so'.
# All others include paths. # All others include paths.
next unless feat.size < 20 && !feat.include?('/') next unless feat.size < 20 && !feat.include?("/")
base = File.basename(feat, '.*') # enumerator.so -> enumerator base = File.basename(feat, ".*") # enumerator.so -> enumerator
ext = File.extname(feat) # .so ext = File.extname(feat) # .so
features[feat] = nil # enumerator.so features[feat] = nil # enumerator.so
features[base] = nil # enumerator features[base] = nil # enumerator
next unless [DOT_SO, *DL_EXTENSIONS].include?(ext) next unless [DOT_SO, *DL_EXTENSIONS].include?(ext)
DL_EXTENSIONS.each do |dl_ext| DL_EXTENSIONS.each do |dl_ext|
features["#{base}#{dl_ext}"] = nil # enumerator.bundle features["#{base}#{dl_ext}"] = nil # enumerator.bundle
end end
@ -50,7 +51,7 @@ module Bootsnap
return feature if Bootsnap.absolute_path?(feature) return feature if Bootsnap.absolute_path?(feature)
if feature.start_with?('./', '../') if feature.start_with?("./", "../")
return try_extensions ? expand_path(feature) : File.expand_path(feature).freeze return try_extensions ? expand_path(feature) : File.expand_path(feature).freeze
end end
@ -64,7 +65,7 @@ module Bootsnap
# returns false as if it were already loaded; however, there is no # returns false as if it were already loaded; however, there is no
# file to find on disk. We've pre-built a list of these, and we # file to find on disk. We've pre-built a list of these, and we
# return false if any of them is loaded. # return false if any of them is loaded.
raise(LoadPathCache::ReturnFalse, '', []) if BUILTIN_FEATURES.key?(feature) raise(LoadPathCache::ReturnFalse, "", []) if BUILTIN_FEATURES.key?(feature)
# The feature wasn't found on our preliminary search through the index. # The feature wasn't found on our preliminary search through the index.
# We resolve this differently depending on what the extension was. # We resolve this differently depending on what the extension was.
@ -73,13 +74,14 @@ module Bootsnap
# native dynamic extension, e.g. .bundle or .so), we know it was a # native dynamic extension, e.g. .bundle or .so), we know it was a
# failure and there's nothing more we can do to find the file. # failure and there's nothing more we can do to find the file.
# no extension, .rb, (.bundle or .so) # no extension, .rb, (.bundle or .so)
when '', *CACHED_EXTENSIONS when "", *CACHED_EXTENSIONS
nil nil
# Ruby allows specifying native extensions as '.so' even when DLEXT # Ruby allows specifying native extensions as '.so' even when DLEXT
# is '.bundle'. This is where we handle that case. # is '.bundle'. This is where we handle that case.
when DOT_SO when DOT_SO
x = search_index(feature[0..-4] + DLEXT) x = search_index(feature[0..-4] + DLEXT)
return x if x return x if x
if DLEXT2 if DLEXT2
x = search_index(feature[0..-4] + DLEXT2) x = search_index(feature[0..-4] + DLEXT2)
return x if x return x if x
@ -87,7 +89,7 @@ module Bootsnap
else else
# other, unknown extension. For example, `.rake`. Since we haven't # other, unknown extension. For example, `.rake`. Since we haven't
# cached these, we legitimately need to run the load path search. # cached these, we legitimately need to run the load path search.
raise(LoadPathCache::FallbackScan, '', []) raise(LoadPathCache::FallbackScan, "", [])
end end
end end
@ -95,16 +97,18 @@ module Bootsnap
# cases where the file doesn't appear to be on the load path. We should # cases where the file doesn't appear to be on the load path. We should
# be able to detect newly-created files without rebooting the # be able to detect newly-created files without rebooting the
# application. # application.
raise(LoadPathCache::FallbackScan, '', []) if @development_mode raise(LoadPathCache::FallbackScan, "", []) if @development_mode
end end
def unshift_paths(sender, *paths) def unshift_paths(sender, *paths)
return unless sender == @path_obj return unless sender == @path_obj
@mutex.synchronize { unshift_paths_locked(*paths) } @mutex.synchronize { unshift_paths_locked(*paths) }
end end
def push_paths(sender, *paths) def push_paths(sender, *paths)
return unless sender == @path_obj return unless sender == @path_obj
@mutex.synchronize { push_paths_locked(*paths) } @mutex.synchronize { push_paths_locked(*paths) }
end end
@ -137,6 +141,7 @@ module Bootsnap
p = Path.new(path) p = Path.new(path)
@has_relative_paths = true if p.relative? @has_relative_paths = true if p.relative?
next if p.non_directory? next if p.non_directory?
expanded_path = p.expanded_path expanded_path = p.expanded_path
entries, dirs = p.entries_and_dirs(@store) entries, dirs = p.entries_and_dirs(@store)
# push -> low precedence -> set only if unset # push -> low precedence -> set only if unset
@ -151,6 +156,7 @@ module Bootsnap
paths.map(&:to_s).reverse_each do |path| paths.map(&:to_s).reverse_each do |path|
p = Path.new(path) p = Path.new(path)
next if p.non_directory? next if p.non_directory?
expanded_path = p.expanded_path expanded_path = p.expanded_path
entries, dirs = p.entries_and_dirs(@store) entries, dirs = p.entries_and_dirs(@store)
# unshift -> high precedence -> unconditional set # unshift -> high precedence -> unconditional set
@ -173,56 +179,62 @@ module Bootsnap
end end
if DLEXT2 if DLEXT2
def search_index(f, try_extensions: true) def search_index(feature, try_extensions: true)
if try_extensions if try_extensions
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f) try_index(feature + DOT_RB) ||
try_index(feature + DLEXT) ||
try_index(feature + DLEXT2) ||
try_index(feature)
else else
try_index(f) try_index(feature)
end end
end end
def maybe_append_extension(f) def maybe_append_extension(feature)
try_ext(f + DOT_RB) || try_ext(f + DLEXT) || try_ext(f + DLEXT2) || f try_ext(feature + DOT_RB) ||
try_ext(feature + DLEXT) ||
try_ext(feature + DLEXT2) ||
feature
end end
else else
def search_index(f, try_extensions: true) def search_index(feature, try_extensions: true)
if try_extensions if try_extensions
try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f) try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
else else
try_index(f) try_index(feature)
end end
end end
def maybe_append_extension(f) def maybe_append_extension(feature)
try_ext(f + DOT_RB) || try_ext(f + DLEXT) || f try_ext(feature + DOT_RB) || try_ext(feature + DLEXT) || feature
end end
end end
s = rand.to_s.force_encoding(Encoding::US_ASCII).freeze s = rand.to_s.force_encoding(Encoding::US_ASCII).freeze
if s.respond_to?(:-@) if s.respond_to?(:-@)
if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= '2.7' if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= "2.7"
def try_index(f) def try_index(feature)
if (p = @index[f]) if (path = @index[feature])
-(File.join(p, f).freeze) -File.join(path, feature).freeze
end end
end end
else else
def try_index(f) def try_index(feature)
if (p = @index[f]) if (path = @index[feature])
-File.join(p, f).untaint -File.join(path, feature).untaint
end end
end end
end end
else else
def try_index(f) def try_index(feature)
if (p = @index[f]) if (path = @index[feature])
File.join(p, f) File.join(path, feature)
end end
end end
end end
def try_ext(f) def try_ext(feature)
f if File.exist?(f) feature if File.exist?(feature)
end end
end end
end end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
module ChangeObserver module ChangeObserver
@ -57,6 +58,7 @@ module Bootsnap
def self.register(observer, arr) def self.register(observer, arr)
return if arr.frozen? # can't register observer, but no need to. return if arr.frozen? # can't register observer, but no need to.
arr.instance_variable_set(:@lpc_observer, observer) arr.instance_variable_set(:@lpc_observer, observer)
arr.extend(ArrayMixin) arr.extend(ArrayMixin)
end end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
module CoreExt module CoreExt
@ -13,7 +14,7 @@ module Bootsnap
end end
module Kernel module Kernel
module_function # rubocop:disable Style/ModuleFunction module_function
alias_method(:require_without_bootsnap, :require) alias_method(:require_without_bootsnap, :require)
@ -32,9 +33,9 @@ module Kernel
end end
raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path)) raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
rescue LoadError => e rescue LoadError => error
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true) error.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
raise(e) raise(error)
rescue Bootsnap::LoadPathCache::ReturnFalse rescue Bootsnap::LoadPathCache::ReturnFalse
false false
rescue Bootsnap::LoadPathCache::FallbackScan rescue Bootsnap::LoadPathCache::FallbackScan
@ -75,9 +76,9 @@ class Module
# added to $LOADED_FEATURES and won't be able to hook that modification # added to $LOADED_FEATURES and won't be able to hook that modification
# since it's done in C-land. # since it's done in C-land.
autoload_without_bootsnap(const, Bootsnap::LoadPathCache.load_path_cache.find(path) || path) autoload_without_bootsnap(const, Bootsnap::LoadPathCache.load_path_cache.find(path) || path)
rescue LoadError => e rescue LoadError => error
e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true) error.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
raise(e) raise(error)
rescue Bootsnap::LoadPathCache::ReturnFalse rescue Bootsnap::LoadPathCache::ReturnFalse
false false
rescue Bootsnap::LoadPathCache::FallbackScan rescue Bootsnap::LoadPathCache::FallbackScan

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
class << $LOADED_FEATURES class << $LOADED_FEATURES
alias_method(:delete_without_bootsnap, :delete) alias_method(:delete_without_bootsnap, :delete)
def delete(key) def delete(key)

View File

@ -29,14 +29,15 @@ module Bootsnap
@mutex = Mutex.new @mutex = Mutex.new
# In theory the user could mutate $LOADED_FEATURES and invalidate our # In theory the user could mutate $LOADED_FEATURES and invalidate our
# cache. If this ever comes up in practice or if you, the # cache. If this ever comes up in practice - or if you, the
# enterprising reader, feels inclined to solve this problem we could # enterprising reader, feels inclined to solve this problem - we could
# parallel the work done with ChangeObserver on $LOAD_PATH to mirror # parallel the work done with ChangeObserver on $LOAD_PATH to mirror
# updates to our @lfi. # updates to our @lfi.
$LOADED_FEATURES.each do |feat| $LOADED_FEATURES.each do |feat|
hash = feat.hash hash = feat.hash
$LOAD_PATH.each do |lpe| $LOAD_PATH.each do |lpe|
next unless feat.start_with?(lpe) next unless feat.start_with?(lpe)
# /a/b/lib/my/foo.rb # /a/b/lib/my/foo.rb
# ^^^^^^^^^ # ^^^^^^^^^
short = feat[(lpe.length + 1)..-1] short = feat[(lpe.length + 1)..-1]
@ -93,7 +94,7 @@ module Bootsnap
ret = yield ret = yield
long = $LOADED_FEATURES[len..-1].detect do |feat| long = $LOADED_FEATURES[len..-1].detect do |feat|
offset = 0 offset = 0
while offset = feat.index(short, offset) while (offset = feat.index(short, offset))
if feat.index(".", offset + 1) && !feat.index("/", offset + 2) if feat.index(".", offset + 1) && !feat.index("/", offset + 2)
break true break true
else else
@ -128,7 +129,7 @@ module Bootsnap
private private
STRIP_EXTENSION = /\.[^.]*?$/ STRIP_EXTENSION = /\.[^.]*?$/.freeze
private_constant(:STRIP_EXTENSION) private_constant(:STRIP_EXTENSION)
# Might Ruby automatically search for this extension if # Might Ruby automatically search for this extension if
@ -145,15 +146,15 @@ module Bootsnap
# with calling a Ruby file 'x.dylib.rb' and then requiring it as 'x.dylib'.) # with calling a Ruby file 'x.dylib.rb' and then requiring it as 'x.dylib'.)
# #
# See <https://ruby-doc.org/core-2.6.4/Kernel.html#method-i-require>. # See <https://ruby-doc.org/core-2.6.4/Kernel.html#method-i-require>.
def extension_elidable?(f) def extension_elidable?(feature)
f.to_s.end_with?('.rb', '.so', '.o', '.dll', '.dylib') feature.to_s.end_with?(".rb", ".so", ".o", ".dll", ".dylib")
end end
def strip_extension_if_elidable(f) def strip_extension_if_elidable(feature)
if extension_elidable?(f) if extension_elidable?(feature)
f.sub(STRIP_EXTENSION, '') feature.sub(STRIP_EXTENSION, "")
else else
f feature
end end
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative('path_scanner')
require_relative("path_scanner")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -43,6 +44,7 @@ module Bootsnap
# set to zero anyway, just in case we change the stability heuristics. # set to zero anyway, just in case we change the stability heuristics.
_, entries, dirs = store.get(expanded_path) _, entries, dirs = store.get(expanded_path)
return [entries, dirs] if entries # cache hit return [entries, dirs] if entries # cache hit
entries, dirs = scan! entries, dirs = scan!
store.set(expanded_path, [0, entries, dirs]) store.set(expanded_path, [0, entries, dirs])
return [entries, dirs] return [entries, dirs]
@ -93,8 +95,8 @@ module Bootsnap
# Built-in ruby lib stuff doesn't change, but things can occasionally be # Built-in ruby lib stuff doesn't change, but things can occasionally be
# installed into sitedir, which generally lives under libdir. # installed into sitedir, which generally lives under libdir.
RUBY_LIBDIR = RbConfig::CONFIG['libdir'] RUBY_LIBDIR = RbConfig::CONFIG["libdir"]
RUBY_SITEDIR = RbConfig::CONFIG['sitedir'] RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
def stability def stability
@stability ||= begin @stability ||= begin

View File

@ -1,18 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative('../explicit_require') require_relative("../explicit_require")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
module PathScanner module PathScanner
REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS
NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO) NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO)
ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/ ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/.freeze
BUNDLE_PATH = if Bootsnap.bundler? BUNDLE_PATH = if Bootsnap.bundler?
(Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze (Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze
else else
'' ""
end end
class << self class << self
@ -44,7 +44,8 @@ module Bootsnap
def walk(absolute_dir_path, relative_dir_path, &block) def walk(absolute_dir_path, relative_dir_path, &block)
Dir.foreach(absolute_dir_path) do |name| Dir.foreach(absolute_dir_path) do |name|
next if name.start_with?('.') next if name.start_with?(".")
relative_path = relative_dir_path ? File.join(relative_dir_path, name) : name relative_path = relative_dir_path ? File.join(relative_dir_path, name) : name
absolute_path = "#{absolute_dir_path}/#{name}" absolute_path = "#{absolute_dir_path}/#{name}"
@ -58,7 +59,7 @@ module Bootsnap
end end
end end
if RUBY_VERSION >= '3.1' if RUBY_VERSION >= "3.1"
def os_path(path) def os_path(path)
path.freeze path.freeze
end end

View File

@ -21,6 +21,7 @@ module Bootsnap
def find_file(name) def find_file(name)
return File.realpath(name).freeze if File.exist?(name) return File.realpath(name).freeze if File.exist?(name)
CACHED_EXTENSIONS.each do |ext| CACHED_EXTENSIONS.each do |ext|
filename = "#{name}#{ext}" filename = "#{name}#{ext}"
return File.realpath(filename).freeze if File.exist?(filename) return File.realpath(filename).freeze if File.exist?(filename)

View File

@ -1,14 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative('../explicit_require')
Bootsnap::ExplicitRequire.with_gems('msgpack') { require('msgpack') } require_relative("../explicit_require")
Bootsnap::ExplicitRequire.from_rubylibdir('fileutils')
Bootsnap::ExplicitRequire.with_gems("msgpack") { require("msgpack") }
Bootsnap::ExplicitRequire.from_rubylibdir("fileutils")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
class Store class Store
VERSION_KEY = '__bootsnap_ruby_version__' VERSION_KEY = "__bootsnap_ruby_version__"
CURRENT_VERSION = "#{RUBY_REVISION}-#{RUBY_PLATFORM}".freeze CURRENT_VERSION = "#{RUBY_REVISION}-#{RUBY_PLATFORM}".freeze # rubocop:disable Style/RedundantFreeze
NestedTransactionError = Class.new(StandardError) NestedTransactionError = Class.new(StandardError)
SetOutsideTransactionNotAllowed = Class.new(StandardError) SetOutsideTransactionNotAllowed = Class.new(StandardError)
@ -26,6 +27,7 @@ module Bootsnap
def fetch(key) def fetch(key)
raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned? raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned?
v = get(key) v = get(key)
unless v unless v
@dirty = true @dirty = true
@ -37,6 +39,7 @@ module Bootsnap
def set(key, value) def set(key, value)
raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned? raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned?
if value != @data[key] if value != @data[key]
@dirty = true @dirty = true
@data[key] = value @data[key] = value
@ -45,6 +48,7 @@ module Bootsnap
def transaction def transaction
raise(NestedTransactionError) if @txn_mutex.owned? raise(NestedTransactionError) if @txn_mutex.owned?
@txn_mutex.synchronize do @txn_mutex.synchronize do
begin begin
yield yield
@ -88,7 +92,7 @@ module Bootsnap
def dump_data def dump_data
# Change contents atomically so other processes can't get invalid # Change contents atomically so other processes can't get invalid
# caches if they read at an inopportune time. # caches if they read at an inopportune time.
tmp = "#{@store_path}.#{Process.pid}.#{(rand * 100000).to_i}.tmp" tmp = "#{@store_path}.#{Process.pid}.#{(rand * 100_000).to_i}.tmp"
FileUtils.mkpath(File.dirname(tmp)) FileUtils.mkpath(File.dirname(tmp))
exclusive_write = File::Constants::CREAT | File::Constants::EXCL | File::Constants::WRONLY exclusive_write = File::Constants::CREAT | File::Constants::EXCL | File::Constants::WRONLY
# `encoding:` looks redundant wrt `binwrite`, but necessary on windows # `encoding:` looks redundant wrt `binwrite`, but necessary on windows
@ -103,7 +107,7 @@ module Bootsnap
end end
def default_data def default_data
{ VERSION_KEY => CURRENT_VERSION } {VERSION_KEY => CURRENT_VERSION}
end end
end end
end end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative('../bootsnap')
require_relative("../bootsnap")
Bootsnap.default_setup Bootsnap.default_setup

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Bootsnap module Bootsnap
VERSION = "1.9.4" VERSION = "1.9.4"
end end

View File

@ -1,17 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class BundlerTest < Minitest::Test class BundlerTest < Minitest::Test
def test_bundler_with_bundle_bin_path_env def test_bundler_with_bundle_bin_path_env
without_required_env_keys do without_required_env_keys do
ENV['BUNDLE_BIN_PATH'] = 'foo' ENV["BUNDLE_BIN_PATH"] = "foo"
assert_predicate(Bootsnap, :bundler?) assert_predicate(Bootsnap, :bundler?)
end end
end end
def test_bundler_with_bundle_gemfile_env def test_bundler_with_bundle_gemfile_env
without_required_env_keys do without_required_env_keys do
ENV['BUNDLE_GEMFILE'] = 'foo' ENV["BUNDLE_GEMFILE"] = "foo"
assert_predicate(Bootsnap, :bundler?) assert_predicate(Bootsnap, :bundler?)
end end
end end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require('bootsnap/cli') require("test_helper")
require("bootsnap/cli")
module Bootsnap module Bootsnap
class CLITest < Minitest::Test class CLITest < Minitest::Test
@ -8,52 +9,52 @@ module Bootsnap
def setup def setup
super super
@cache_dir = File.expand_path('tmp/cache/bootsnap/compile-cache') @cache_dir = File.expand_path("tmp/cache/bootsnap/compile-cache")
end end
def test_precompile_single_file def test_precompile_single_file
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
CompileCache::ISeq.expects(:precompile).with(File.expand_path(path), cache_dir: @cache_dir) CompileCache::ISeq.expects(:precompile).with(File.expand_path(path), cache_dir: @cache_dir)
assert_equal 0, CLI.new(['precompile', '-j', '0', path]).run assert_equal 0, CLI.new(["precompile", "-j", "0", path]).run
end end
def test_no_iseq def test_no_iseq
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
CompileCache::ISeq.expects(:precompile).never CompileCache::ISeq.expects(:precompile).never
assert_equal 0, CLI.new(['precompile', '-j', '0', '--no-iseq', path]).run assert_equal 0, CLI.new(["precompile", "-j", "0", "--no-iseq", path]).run
end end
def test_precompile_directory def test_precompile_directory
path_a = Help.set_file('foo/a.rb', 'a = a = 3', 100) path_a = Help.set_file("foo/a.rb", "a = a = 3", 100)
path_b = Help.set_file('foo/b.rb', 'b = b = 3', 100) path_b = Help.set_file("foo/b.rb", "b = b = 3", 100)
CompileCache::ISeq.expects(:precompile).with(File.expand_path(path_a), cache_dir: @cache_dir) CompileCache::ISeq.expects(:precompile).with(File.expand_path(path_a), cache_dir: @cache_dir)
CompileCache::ISeq.expects(:precompile).with(File.expand_path(path_b), cache_dir: @cache_dir) CompileCache::ISeq.expects(:precompile).with(File.expand_path(path_b), cache_dir: @cache_dir)
assert_equal 0, CLI.new(['precompile', '-j', '0', 'foo']).run assert_equal 0, CLI.new(["precompile", "-j", "0", "foo"]).run
end end
def test_precompile_exclude def test_precompile_exclude
path_a = Help.set_file('foo/a.rb', 'a = a = 3', 100) path_a = Help.set_file("foo/a.rb", "a = a = 3", 100)
Help.set_file('foo/b.rb', 'b = b = 3', 100) Help.set_file("foo/b.rb", "b = b = 3", 100)
CompileCache::ISeq.expects(:precompile).with(File.expand_path(path_a), cache_dir: @cache_dir) CompileCache::ISeq.expects(:precompile).with(File.expand_path(path_a), cache_dir: @cache_dir)
assert_equal 0, CLI.new(['precompile', '-j', '0', '--exclude', 'b.rb', 'foo']).run assert_equal 0, CLI.new(["precompile", "-j", "0", "--exclude", "b.rb", "foo"]).run
end end
def test_precompile_gemfile def test_precompile_gemfile
assert_equal 0, CLI.new(['precompile', '--gemfile']).run assert_equal 0, CLI.new(["precompile", "--gemfile"]).run
end end
def test_precompile_yaml def test_precompile_yaml
path = Help.set_file('a.yaml', 'foo: bar', 100) path = Help.set_file("a.yaml", "foo: bar", 100)
CompileCache::YAML.expects(:precompile).with(File.expand_path(path), cache_dir: @cache_dir) CompileCache::YAML.expects(:precompile).with(File.expand_path(path), cache_dir: @cache_dir)
assert_equal 0, CLI.new(['precompile', '-j', '0', path]).run assert_equal 0, CLI.new(["precompile", "-j", "0", path]).run
end end
def test_no_yaml def test_no_yaml
path = Help.set_file('a.yaml', 'foo: bar', 100) path = Help.set_file("a.yaml", "foo: bar", 100)
CompileCache::YAML.expects(:precompile).never CompileCache::YAML.expects(:precompile).never
assert_equal 0, CLI.new(['precompile', '-j', '0', '--no-yaml', path]).run assert_equal 0, CLI.new(["precompile", "-j", "0", "--no-yaml", path]).run
end end
end end
end end

View File

@ -1,11 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class CompileCacheISeqTest < Minitest::Test class CompileCacheISeqTest < Minitest::Test
include(TmpdirHelper) include(TmpdirHelper)
def test_ruby_bug_18250 def test_ruby_bug_18250
Help.set_file('a.rb', 'def foo(*); ->{ super }; end; def foo(**); ->{ super }; end', 100) Help.set_file("a.rb", "def foo(*); ->{ super }; end; def foo(**); ->{ super }; end", 100)
Bootsnap::CompileCache::ISeq.fetch('a.rb') Bootsnap::CompileCache::ISeq.fetch("a.rb")
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class CompileCacheJSONTest < Minitest::Test class CompileCacheJSONTest < Minitest::Test
include(TmpdirHelper) include(TmpdirHelper)
@ -7,7 +8,7 @@ class CompileCacheJSONTest < Minitest::Test
module FakeJson module FakeJson
Fallback = Class.new(StandardError) Fallback = Class.new(StandardError)
class << self class << self
def load_file(path, symbolize_names: false, freeze: false, fallback: nil) def load_file(_path, symbolize_names: false, freeze: false, fallback: nil)
raise Fallback raise Fallback
end end
end end
@ -27,55 +28,55 @@ class CompileCacheJSONTest < Minitest::Test
} }
JSON JSON
expected = { expected = {
'foo' => 42, "foo" => 42,
'bar' => [1], "bar" => [1],
} }
assert_equal expected, document assert_equal expected, document
end end
def test_load_file def test_load_file
Help.set_file('a.json', '{"foo": "bar"}', 100) Help.set_file("a.json", '{"foo": "bar"}', 100)
assert_equal({'foo' => 'bar'}, FakeJson.load_file('a.json')) assert_equal({"foo" => "bar"}, FakeJson.load_file("a.json"))
end end
def test_load_file_symbolize_names def test_load_file_symbolize_names
Help.set_file('a.json', '{"foo": "bar"}', 100) Help.set_file("a.json", '{"foo": "bar"}', 100)
FakeJson.load_file('a.json') FakeJson.load_file("a.json")
if ::Bootsnap::CompileCache::JSON.supported_options.include?(:symbolize_names) if ::Bootsnap::CompileCache::JSON.supported_options.include?(:symbolize_names)
2.times do 2.times do
assert_equal({foo: 'bar'}, FakeJson.load_file('a.json', symbolize_names: true)) assert_equal({foo: "bar"}, FakeJson.load_file("a.json", symbolize_names: true))
end end
else else
assert_raises(FakeJson::Fallback) do # would call super assert_raises(FakeJson::Fallback) do # would call super
FakeJson.load_file('a.json', symbolize_names: true) FakeJson.load_file("a.json", symbolize_names: true)
end end
end end
end end
def test_load_file_freeze def test_load_file_freeze
Help.set_file('a.json', '["foo"]', 100) Help.set_file("a.json", '["foo"]', 100)
FakeJson.load_file('a.json') FakeJson.load_file("a.json")
if ::Bootsnap::CompileCache::JSON.supported_options.include?(:freeze) if ::Bootsnap::CompileCache::JSON.supported_options.include?(:freeze)
2.times do 2.times do
string = FakeJson.load_file('a.json', freeze: true).first string = FakeJson.load_file("a.json", freeze: true).first
assert_equal("foo", string) assert_equal("foo", string)
assert_predicate(string, :frozen?) assert_predicate(string, :frozen?)
end end
else else
assert_raises(FakeJson::Fallback) do # would call super assert_raises(FakeJson::Fallback) do # would call super
FakeJson.load_file('a.json', freeze: true) FakeJson.load_file("a.json", freeze: true)
end end
end end
end end
def test_load_file_unknown_option def test_load_file_unknown_option
Help.set_file('a.json', '["foo"]', 100) Help.set_file("a.json", '["foo"]', 100)
FakeJson.load_file('a.json') FakeJson.load_file("a.json")
assert_raises(FakeJson::Fallback) do # would call super assert_raises(FakeJson::Fallback) do # would call super
FakeJson.load_file('a.json', fallback: true) FakeJson.load_file("a.json", fallback: true)
end end
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class CompileCacheYAMLTest < Minitest::Test class CompileCacheYAMLTest < Minitest::Test
include(TmpdirHelper) include(TmpdirHelper)
@ -7,11 +8,11 @@ class CompileCacheYAMLTest < Minitest::Test
module FakeYaml module FakeYaml
Fallback = Class.new(StandardError) Fallback = Class.new(StandardError)
class << self class << self
def load_file(path, symbolize_names: false, freeze: false, fallback: nil) def load_file(_path, symbolize_names: false, freeze: false, fallback: nil)
raise Fallback raise Fallback
end end
def unsafe_load_file(path, symbolize_names: false, freeze: false, fallback: nil) def unsafe_load_file(_path, symbolize_names: false, freeze: false, fallback: nil)
raise Fallback raise Fallback
end end
end end
@ -31,7 +32,7 @@ class CompileCacheYAMLTest < Minitest::Test
YAML YAML
expected = { expected = {
foo: 42, foo: 42,
'bar' => [1], "bar" => [1],
} }
assert_equal expected, document assert_equal expected, document
end end
@ -44,89 +45,89 @@ class CompileCacheYAMLTest < Minitest::Test
YAML YAML
expected = { expected = {
foo: 42, foo: 42,
'bar' => [1], "bar" => [1],
} }
assert_equal expected, document assert_equal expected, document
end end
def test_yaml_tags def test_yaml_tags
error = assert_raises Bootsnap::CompileCache::Uncompilable do error = assert_raises Bootsnap::CompileCache::Uncompilable do
::Bootsnap::CompileCache::YAML.strict_load('!many Boolean') ::Bootsnap::CompileCache::YAML.strict_load("!many Boolean")
end end
assert_equal "YAML tags are not supported: !many", error.message assert_equal "YAML tags are not supported: !many", error.message
error = assert_raises Bootsnap::CompileCache::Uncompilable do error = assert_raises Bootsnap::CompileCache::Uncompilable do
::Bootsnap::CompileCache::YAML.strict_load('!ruby/object {}') ::Bootsnap::CompileCache::YAML.strict_load("!ruby/object {}")
end end
assert_equal "YAML tags are not supported: !ruby/object", error.message assert_equal "YAML tags are not supported: !ruby/object", error.message
end end
if YAML::VERSION >= '4' if YAML::VERSION >= "4"
def test_load_psych_4 def test_load_psych_4
# Until we figure out a proper strategy, only `YAML.unsafe_load_file` # Until we figure out a proper strategy, only `YAML.unsafe_load_file`
# is cached with Psych >= 4 # is cached with Psych >= 4
Help.set_file('a.yml', "foo: &foo\n bar: 42\nplop:\n <<: *foo", 100) Help.set_file("a.yml", "foo: &foo\n bar: 42\nplop:\n <<: *foo", 100)
assert_raises FakeYaml::Fallback do assert_raises FakeYaml::Fallback do
FakeYaml.load_file('a.yml') FakeYaml.load_file("a.yml")
end end
end end
else else
def test_load_file def test_load_file
Help.set_file('a.yml', "---\nfoo: bar", 100) Help.set_file("a.yml", "---\nfoo: bar", 100)
assert_equal({'foo' => 'bar'}, FakeYaml.load_file('a.yml')) assert_equal({"foo" => "bar"}, FakeYaml.load_file("a.yml"))
end end
def test_load_file_aliases def test_load_file_aliases
Help.set_file('a.yml', "foo: &foo\n bar: 42\nplop:\n <<: *foo", 100) Help.set_file("a.yml", "foo: &foo\n bar: 42\nplop:\n <<: *foo", 100)
assert_equal({"foo" => { "bar" => 42 }, "plop" => { "bar" => 42} }, FakeYaml.load_file('a.yml')) assert_equal({"foo" => {"bar" => 42}, "plop" => {"bar" => 42}}, FakeYaml.load_file("a.yml"))
end end
def test_load_file_symbolize_names def test_load_file_symbolize_names
Help.set_file('a.yml', "---\nfoo: bar", 100) Help.set_file("a.yml", "---\nfoo: bar", 100)
FakeYaml.load_file('a.yml') FakeYaml.load_file("a.yml")
if ::Bootsnap::CompileCache::YAML.supported_options.include?(:symbolize_names) if ::Bootsnap::CompileCache::YAML.supported_options.include?(:symbolize_names)
2.times do 2.times do
assert_equal({foo: 'bar'}, FakeYaml.load_file('a.yml', symbolize_names: true)) assert_equal({foo: "bar"}, FakeYaml.load_file("a.yml", symbolize_names: true))
end end
else else
assert_raises(FakeYaml::Fallback) do # would call super assert_raises(FakeYaml::Fallback) do # would call super
FakeYaml.load_file('a.yml', symbolize_names: true) FakeYaml.load_file("a.yml", symbolize_names: true)
end end
end end
end end
def test_load_file_freeze def test_load_file_freeze
Help.set_file('a.yml', "---\nfoo", 100) Help.set_file("a.yml", "---\nfoo", 100)
FakeYaml.load_file('a.yml') FakeYaml.load_file("a.yml")
if ::Bootsnap::CompileCache::YAML.supported_options.include?(:freeze) if ::Bootsnap::CompileCache::YAML.supported_options.include?(:freeze)
2.times do 2.times do
string = FakeYaml.load_file('a.yml', freeze: true) string = FakeYaml.load_file("a.yml", freeze: true)
assert_equal("foo", string) assert_equal("foo", string)
assert_predicate(string, :frozen?) assert_predicate(string, :frozen?)
end end
else else
assert_raises(FakeYaml::Fallback) do # would call super assert_raises(FakeYaml::Fallback) do # would call super
FakeYaml.load_file('a.yml', freeze: true) FakeYaml.load_file("a.yml", freeze: true)
end end
end end
end end
def test_load_file_unknown_option def test_load_file_unknown_option
Help.set_file('a.yml', "---\nfoo", 100) Help.set_file("a.yml", "---\nfoo", 100)
FakeYaml.load_file('a.yml') FakeYaml.load_file("a.yml")
assert_raises(FakeYaml::Fallback) do # would call super assert_raises(FakeYaml::Fallback) do # would call super
FakeYaml.load_file('a.yml', fallback: true) FakeYaml.load_file("a.yml", fallback: true)
end end
end end
end end
if YAML.respond_to?(:unsafe_load_file) if YAML.respond_to?(:unsafe_load_file)
def test_unsafe_load_file def test_unsafe_load_file
Help.set_file('a.yml', "foo: &foo\n bar: 42\nplop:\n <<: *foo", 100) Help.set_file("a.yml", "foo: &foo\n bar: 42\nplop:\n <<: *foo", 100)
assert_equal({"foo" => { "bar" => 42 }, "plop" => { "bar" => 42} }, FakeYaml.unsafe_load_file('a.yml')) assert_equal({"foo" => {"bar" => 42}, "plop" => {"bar" => 42}}, FakeYaml.unsafe_load_file("a.yml"))
end end
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class CompileCacheHandlerErrorsTest < Minitest::Test class CompileCacheHandlerErrorsTest < Minitest::Test
include(TmpdirHelper) include(TmpdirHelper)
@ -10,16 +11,16 @@ class CompileCacheHandlerErrorsTest < Minitest::Test
# 3. exception # 3. exception
def test_input_to_storage_unexpected_type def test_input_to_storage_unexpected_type
path = Help.set_file('a.rb', 'a = 3', 100) path = Help.set_file("a.rb", "a = 3", 100)
Bootsnap::CompileCache::ISeq.expects(:input_to_storage).returns(nil) Bootsnap::CompileCache::ISeq.expects(:input_to_storage).returns(nil)
# this could be made slightly more obvious though. # this could be made slightly more obvious though.
assert_raises(TypeError) { load(path) } assert_raises(TypeError) { load(path) }
end end
def test_input_to_storage_invalid_instance_of_expected_type def test_input_to_storage_invalid_instance_of_expected_type
path = Help.set_file('a.rb', 'a = 3', 100) path = Help.set_file("a.rb", "a = 3", 100)
Bootsnap::CompileCache::ISeq.expects(:input_to_storage).returns('broken') Bootsnap::CompileCache::ISeq.expects(:input_to_storage).returns("broken")
Bootsnap::CompileCache::ISeq.expects(:input_to_output).with('a = 3', nil).returns('whatever') Bootsnap::CompileCache::ISeq.expects(:input_to_output).with("a = 3", nil).returns("whatever")
_, err = capture_subprocess_io do _, err = capture_subprocess_io do
load(path) load(path)
end end
@ -27,14 +28,14 @@ class CompileCacheHandlerErrorsTest < Minitest::Test
end end
def test_input_to_storage_raises def test_input_to_storage_raises
path = Help.set_file('a.rb', 'a = 3', 100) path = Help.set_file("a.rb", "a = 3", 100)
klass = Class.new(StandardError) klass = Class.new(StandardError)
Bootsnap::CompileCache::ISeq.expects(:input_to_storage).raises(klass, 'oops') Bootsnap::CompileCache::ISeq.expects(:input_to_storage).raises(klass, "oops")
assert_raises(klass) { load(path) } assert_raises(klass) { load(path) }
end end
def test_storage_to_output_unexpected_type def test_storage_to_output_unexpected_type
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
Bootsnap::CompileCache::ISeq.expects(:storage_to_output).returns(Object.new) Bootsnap::CompileCache::ISeq.expects(:storage_to_output).returns(Object.new)
# It seems like ruby doesn't really care. # It seems like ruby doesn't really care.
load(path) load(path)
@ -45,16 +46,16 @@ class CompileCacheHandlerErrorsTest < Minitest::Test
# def test_storage_to_output_invalid_instance_of_expected_type # def test_storage_to_output_invalid_instance_of_expected_type
def test_storage_to_output_raises def test_storage_to_output_raises
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
klass = Class.new(StandardError) klass = Class.new(StandardError)
Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).raises(klass, 'oops') Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).raises(klass, "oops")
assert_raises(klass) { load(path) } assert_raises(klass) { load(path) }
# called from two paths; this tests the second. # called from two paths; this tests the second.
assert_raises(klass) { load(path) } assert_raises(klass) { load(path) }
end end
def test_input_to_output_unexpected_type def test_input_to_output_unexpected_type
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
Bootsnap::CompileCache::ISeq.expects(:input_to_storage).raises(Bootsnap::CompileCache::Uncompilable) Bootsnap::CompileCache::ISeq.expects(:input_to_storage).raises(Bootsnap::CompileCache::Uncompilable)
Bootsnap::CompileCache::ISeq.expects(:input_to_output).returns(Object.new) Bootsnap::CompileCache::ISeq.expects(:input_to_output).returns(Object.new)
# It seems like ruby doesn't really care. # It seems like ruby doesn't really care.
@ -66,10 +67,10 @@ class CompileCacheHandlerErrorsTest < Minitest::Test
# def test_input_to_output_invalid_instance_of_expected_type # def test_input_to_output_invalid_instance_of_expected_type
def test_input_to_output_raises def test_input_to_output_raises
path = Help.set_file('a.rb', 'a = 3', 100) path = Help.set_file("a.rb", "a = 3", 100)
klass = Class.new(StandardError) klass = Class.new(StandardError)
Bootsnap::CompileCache::ISeq.expects(:input_to_storage).raises(Bootsnap::CompileCache::Uncompilable) Bootsnap::CompileCache::ISeq.expects(:input_to_storage).raises(Bootsnap::CompileCache::Uncompilable)
Bootsnap::CompileCache::ISeq.expects(:input_to_output).raises(klass, 'oops') Bootsnap::CompileCache::ISeq.expects(:input_to_output).raises(klass, "oops")
assert_raises(klass) { load(path) } assert_raises(klass) { load(path) }
end end
end end

View File

@ -1,8 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require('tempfile') require("test_helper")
require('tmpdir') require("tempfile")
require('fileutils') require("tmpdir")
require("fileutils")
class CompileCacheKeyFormatTest < Minitest::Test class CompileCacheKeyFormatTest < Minitest::Test
FILE = File.expand_path(__FILE__) FILE = File.expand_path(__FILE__)
@ -16,7 +17,7 @@ class CompileCacheKeyFormatTest < Minitest::Test
size: 16...24, size: 16...24,
mtime: 24...32, mtime: 24...32,
data_size: 32...40, data_size: 32...40,
} }.freeze
def test_key_version def test_key_version
key = cache_key_for_file(FILE) key = cache_key_for_file(FILE)
@ -27,12 +28,12 @@ class CompileCacheKeyFormatTest < Minitest::Test
def test_key_compile_option_stable def test_key_compile_option_stable
k1 = cache_key_for_file(FILE) k1 = cache_key_for_file(FILE)
k2 = cache_key_for_file(FILE) k2 = cache_key_for_file(FILE)
RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true } RubyVM::InstructionSequence.compile_option = {tailcall_optimization: true}
k3 = cache_key_for_file(FILE) k3 = cache_key_for_file(FILE)
assert_equal(k1[R[:compile_option]], k2[R[:compile_option]]) assert_equal(k1[R[:compile_option]], k2[R[:compile_option]])
refute_equal(k1[R[:compile_option]], k3[R[:compile_option]]) refute_equal(k1[R[:compile_option]], k3[R[:compile_option]])
ensure ensure
RubyVM::InstructionSequence.compile_option = { tailcall_optimization: false } RubyVM::InstructionSequence.compile_option = {tailcall_optimization: false}
end end
def test_key_ruby_revision def test_key_ruby_revision
@ -60,11 +61,11 @@ class CompileCacheKeyFormatTest < Minitest::Test
end end
def test_fetch def test_fetch
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
target = 'NUL' target = "NUL"
expected_file = "#{@tmp_dir}/36/9eba19c29ffe00" expected_file = "#{@tmp_dir}/36/9eba19c29ffe00"
else else
target = '/dev/null' target = "/dev/null"
expected_file = "#{@tmp_dir}/8c/d2d180bbd995df" expected_file = "#{@tmp_dir}/8c/d2d180bbd995df"
end end
@ -80,7 +81,7 @@ class CompileCacheKeyFormatTest < Minitest::Test
def test_unexistent_fetch def test_unexistent_fetch
assert_raises(Errno::ENOENT) do assert_raises(Errno::ENOENT) do
Bootsnap::CompileCache::Native.fetch(@tmp_dir, '123', Bootsnap::CompileCache::ISeq, nil) Bootsnap::CompileCache::Native.fetch(@tmp_dir, "123", Bootsnap::CompileCache::ISeq, nil)
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class CompileCacheTest < Minitest::Test class CompileCacheTest < Minitest::Test
include(TmpdirHelper) include(TmpdirHelper)
@ -14,7 +15,7 @@ class CompileCacheTest < Minitest::Test
def test_coverage_running? def test_coverage_running?
refute(Bootsnap::CompileCache::Native.coverage_running?) refute(Bootsnap::CompileCache::Native.coverage_running?)
require('coverage') require("coverage")
begin begin
Coverage.start Coverage.start
assert(Bootsnap::CompileCache::Native.coverage_running?) assert(Bootsnap::CompileCache::Native.coverage_running?)
@ -24,7 +25,7 @@ class CompileCacheTest < Minitest::Test
end end
def test_no_write_permission_to_cache def test_no_write_permission_to_cache
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
# Always pass this test on Windows because directories aren't read, only # Always pass this test on Windows because directories aren't read, only
# listed. You can restrict the ability to list directory contents on # listed. You can restrict the ability to list directory contents on
# Windows or you can set ACLS on a folder such that it is not allowed to # Windows or you can set ACLS on a folder such that it is not allowed to
@ -36,25 +37,25 @@ class CompileCacheTest < Minitest::Test
# read-only files. # read-only files.
pass pass
else else
path = Help.set_file('a.rb', 'a = 3', 100) path = Help.set_file("a.rb", "a = 3", 100)
folder = File.dirname(Help.cache_path(@tmp_dir, path)) folder = File.dirname(Help.cache_path(@tmp_dir, path))
FileUtils.mkdir_p(folder) FileUtils.mkdir_p(folder)
FileUtils.chmod(0400, folder) FileUtils.chmod(0o400, folder)
load(path) load(path)
end end
end end
def test_can_open_read_only_cache def test_can_open_read_only_cache
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
# Load once to create the cache file # Load once to create the cache file
load(path) load(path)
FileUtils.chmod(0400, path) FileUtils.chmod(0o400, path)
# Loading again after the file is marked read-only should still succeed # Loading again after the file is marked read-only should still succeed
load(path) load(path)
end end
def test_file_is_only_read_once def test_file_is_only_read_once
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
storage = RubyVM::InstructionSequence.compile_file(path).to_binary storage = RubyVM::InstructionSequence.compile_file(path).to_binary
output = RubyVM::InstructionSequence.load_from_binary(storage) output = RubyVM::InstructionSequence.load_from_binary(storage)
# This doesn't really *prove* the file is only read once, but # This doesn't really *prove* the file is only read once, but
@ -66,7 +67,7 @@ class CompileCacheTest < Minitest::Test
end end
def test_raises_syntax_error def test_raises_syntax_error
path = Help.set_file('a.rb', 'a = (3', 100) path = Help.set_file("a.rb", "a = (3", 100)
assert_raises(SyntaxError) do assert_raises(SyntaxError) do
# SyntaxError emits directly to stderr in addition to raising, it seems. # SyntaxError emits directly to stderr in addition to raising, it seems.
capture_io { load(path) } capture_io { load(path) }
@ -74,19 +75,19 @@ class CompileCacheTest < Minitest::Test
end end
def test_no_recache_when_mtime_and_size_same def test_no_recache_when_mtime_and_size_same
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
storage = RubyVM::InstructionSequence.compile_file(path).to_binary storage = RubyVM::InstructionSequence.compile_file(path).to_binary
output = RubyVM::InstructionSequence.load_from_binary(storage) output = RubyVM::InstructionSequence.load_from_binary(storage)
Bootsnap::CompileCache::ISeq.expects(:input_to_storage).times(1).returns(storage) Bootsnap::CompileCache::ISeq.expects(:input_to_storage).times(1).returns(storage)
Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).returns(output) Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).returns(output)
load(path) load(path)
Help.set_file(path, 'a = a = 4', 100) Help.set_file(path, "a = a = 4", 100)
load(path) load(path)
end end
def test_recache_when_mtime_different def test_recache_when_mtime_different
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
storage = RubyVM::InstructionSequence.compile_file(path).to_binary storage = RubyVM::InstructionSequence.compile_file(path).to_binary
output = RubyVM::InstructionSequence.load_from_binary(storage) output = RubyVM::InstructionSequence.load_from_binary(storage)
# Totally lies the second time but that's not the point. # Totally lies the second time but that's not the point.
@ -94,12 +95,12 @@ class CompileCacheTest < Minitest::Test
Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).returns(output) Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).returns(output)
load(path) load(path)
Help.set_file(path, 'a = a = 2', 101) Help.set_file(path, "a = a = 2", 101)
load(path) load(path)
end end
def test_recache_when_size_different def test_recache_when_size_different
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
storage = RubyVM::InstructionSequence.compile_file(path).to_binary storage = RubyVM::InstructionSequence.compile_file(path).to_binary
output = RubyVM::InstructionSequence.load_from_binary(storage) output = RubyVM::InstructionSequence.load_from_binary(storage)
# Totally lies the second time but that's not the point. # Totally lies the second time but that's not the point.
@ -107,27 +108,27 @@ class CompileCacheTest < Minitest::Test
Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).returns(output) Bootsnap::CompileCache::ISeq.expects(:storage_to_output).times(2).returns(output)
load(path) load(path)
Help.set_file(path, 'a = 33', 100) Help.set_file(path, "a = 33", 100)
load(path) load(path)
end end
def test_invalid_cache_file def test_invalid_cache_file
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
cp = Help.cache_path(@tmp_dir, path) cp = Help.cache_path(@tmp_dir, path)
FileUtils.mkdir_p(File.dirname(cp)) FileUtils.mkdir_p(File.dirname(cp))
File.write(cp, 'nope') File.write(cp, "nope")
load(path) load(path)
assert(File.size(cp) > 32) # cache was overwritten assert(File.size(cp) > 32) # cache was overwritten
end end
def test_instrumentation_hit def test_instrumentation_hit
path = Help.set_file('a.rb', 'a = a = 3', 100) file_path = Help.set_file("a.rb", "a = a = 3", 100)
load(path) load(file_path)
calls = [] calls = []
Bootsnap.instrumentation = ->(event, path) { calls << [event, path] } Bootsnap.instrumentation = ->(event, path) { calls << [event, path] }
load(path) load(file_path)
assert_equal [], calls assert_equal [], calls
ensure ensure
@ -135,29 +136,29 @@ class CompileCacheTest < Minitest::Test
end end
def test_instrumentation_miss def test_instrumentation_miss
path = Help.set_file('a.rb', 'a = a = 3', 100) file_path = Help.set_file("a.rb", "a = a = 3", 100)
calls = [] calls = []
Bootsnap.instrumentation = ->(event, path) { calls << [event, path] } Bootsnap.instrumentation = ->(event, path) { calls << [event, path] }
load(path) load(file_path)
assert_equal [[:miss, 'a.rb']], calls assert_equal [[:miss, "a.rb"]], calls
ensure ensure
Bootsnap.instrumentation = nil Bootsnap.instrumentation = nil
end end
def test_instrumentation_stale def test_instrumentation_stale
path = Help.set_file('a.rb', 'a = a = 3', 100) file_path = Help.set_file("a.rb", "a = a = 3", 100)
load(path) load(file_path)
path = Help.set_file('a.rb', 'a = a = 4', 101) file_path = Help.set_file("a.rb", "a = a = 4", 101)
calls = [] calls = []
Bootsnap.instrumentation = ->(event, path) { calls << [event, path] } Bootsnap.instrumentation = ->(event, path) { calls << [event, path] }
load(path) load(file_path)
assert_equal [[:stale, 'a.rb']], calls assert_equal [[:stale, "a.rb"]], calls
ensure ensure
Bootsnap.instrumentation = nil Bootsnap.instrumentation = nil
end end

View File

@ -1,11 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
class HelperTest < MiniTest::Test class HelperTest < MiniTest::Test
include(TmpdirHelper) include(TmpdirHelper)
def test_validate_cache_path def test_validate_cache_path
path = Help.set_file('a.rb', 'a = a = 3', 100) path = Help.set_file("a.rb", "a = a = 3", 100)
cp = Help.cache_path(@tmp_dir, path) cp = Help.cache_path(@tmp_dir, path)
load(path) load(path)
assert_equal(true, File.file?(cp)) assert_equal(true, File.file?(cp))

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -28,31 +29,31 @@ module Bootsnap
# versions aren't a big deal, but feel free to fix the test. # versions aren't a big deal, but feel free to fix the test.
def test_builtin_features def test_builtin_features
cache = Cache.new(NullCache, []) cache = Cache.new(NullCache, [])
assert_raises(ReturnFalse) { cache.find('thread') } assert_raises(ReturnFalse) { cache.find("thread") }
assert_raises(ReturnFalse) { cache.find('thread.rb') } assert_raises(ReturnFalse) { cache.find("thread.rb") }
assert_raises(ReturnFalse) { cache.find('enumerator') } assert_raises(ReturnFalse) { cache.find("enumerator") }
assert_raises(ReturnFalse) { cache.find('enumerator.so') } assert_raises(ReturnFalse) { cache.find("enumerator.so") }
if RUBY_PLATFORM =~ /darwin/ if RUBY_PLATFORM =~ /darwin/
assert_raises(ReturnFalse) { cache.find('enumerator.bundle') } assert_raises(ReturnFalse) { cache.find("enumerator.bundle") }
else else
assert_raises(FallbackScan) { cache.find('enumerator.bundle') } assert_raises(FallbackScan) { cache.find("enumerator.bundle") }
end end
bundle = RUBY_PLATFORM =~ /darwin/ ? 'bundle' : 'so' bundle = RUBY_PLATFORM =~ /darwin/ ? "bundle" : "so"
refute(cache.find('thread.' + bundle)) refute(cache.find("thread." + bundle))
refute(cache.find('enumerator.rb')) refute(cache.find("enumerator.rb"))
refute(cache.find('encdb.' + bundle)) refute(cache.find("encdb." + bundle))
end end
def test_simple def test_simple
po = [@dir1] po = [@dir1]
cache = Cache.new(NullCache, po) cache = Cache.new(NullCache, po)
assert_equal("#{@dir1}/a.rb", cache.find('a')) assert_equal("#{@dir1}/a.rb", cache.find("a"))
refute(cache.find('a', try_extensions: false)) refute(cache.find("a", try_extensions: false))
cache.push_paths(po, @dir2) cache.push_paths(po, @dir2)
assert_equal("#{@dir2}/b.rb", cache.find('b')) assert_equal("#{@dir2}/b.rb", cache.find("b"))
refute(cache.find('b', try_extensions: false)) refute(cache.find("b", try_extensions: false))
end end
def test_extension_append_for_relative_paths def test_extension_append_for_relative_paths
@ -60,35 +61,35 @@ module Bootsnap
cache = Cache.new(NullCache, po) cache = Cache.new(NullCache, po)
dir1_basename = File.basename(@dir1) dir1_basename = File.basename(@dir1)
Dir.chdir(@dir1) do Dir.chdir(@dir1) do
assert_equal("#{@dir1}/a.rb", cache.find('./a')) assert_equal("#{@dir1}/a.rb", cache.find("./a"))
assert_equal("#{@dir1}/a", cache.find('./a', try_extensions: false)) assert_equal("#{@dir1}/a", cache.find("./a", try_extensions: false))
assert_equal("#{@dir1}/a.rb", cache.find("../#{dir1_basename}/a")) assert_equal("#{@dir1}/a.rb", cache.find("../#{dir1_basename}/a"))
assert_equal("#{@dir1}/a", cache.find("../#{dir1_basename}/a", try_extensions: false)) assert_equal("#{@dir1}/a", cache.find("../#{dir1_basename}/a", try_extensions: false))
assert_equal("#{@dir1}/dl#{DLEXT}", cache.find('./dl')) assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("./dl"))
assert_equal("#{@dir1}/dl", cache.find('./dl', try_extensions: false)) assert_equal("#{@dir1}/dl", cache.find("./dl", try_extensions: false))
assert_equal("#{@dir1}/enoent", cache.find('./enoent')) assert_equal("#{@dir1}/enoent", cache.find("./enoent"))
assert_equal("#{@dir1}/enoent", cache.find('./enoent', try_extensions: false)) assert_equal("#{@dir1}/enoent", cache.find("./enoent", try_extensions: false))
end end
end end
def test_unshifted_paths_have_higher_precedence def test_unshifted_paths_have_higher_precedence
po = [@dir1] po = [@dir1]
cache = Cache.new(NullCache, po) cache = Cache.new(NullCache, po)
assert_equal("#{@dir1}/conflict.rb", cache.find('conflict')) assert_equal("#{@dir1}/conflict.rb", cache.find("conflict"))
assert_equal("#{@dir1}/conflict.rb", cache.find('conflict.rb', try_extensions: false)) assert_equal("#{@dir1}/conflict.rb", cache.find("conflict.rb", try_extensions: false))
cache.unshift_paths(po, @dir2) cache.unshift_paths(po, @dir2)
assert_equal("#{@dir2}/conflict.rb", cache.find('conflict')) assert_equal("#{@dir2}/conflict.rb", cache.find("conflict"))
assert_equal("#{@dir2}/conflict.rb", cache.find('conflict.rb', try_extensions: false)) assert_equal("#{@dir2}/conflict.rb", cache.find("conflict.rb", try_extensions: false))
end end
def test_pushed_paths_have_lower_precedence def test_pushed_paths_have_lower_precedence
po = [@dir1] po = [@dir1]
cache = Cache.new(NullCache, po) cache = Cache.new(NullCache, po)
assert_equal("#{@dir1}/conflict.rb", cache.find('conflict')) assert_equal("#{@dir1}/conflict.rb", cache.find("conflict"))
assert_equal("#{@dir1}/conflict.rb", cache.find('conflict.rb', try_extensions: false)) assert_equal("#{@dir1}/conflict.rb", cache.find("conflict.rb", try_extensions: false))
cache.push_paths(po, @dir2) cache.push_paths(po, @dir2)
assert_equal("#{@dir1}/conflict.rb", cache.find('conflict')) assert_equal("#{@dir1}/conflict.rb", cache.find("conflict"))
assert_equal("#{@dir1}/conflict.rb", cache.find('conflict.rb', try_extensions: false)) assert_equal("#{@dir1}/conflict.rb", cache.find("conflict.rb", try_extensions: false))
end end
def test_directory_caching def test_directory_caching
@ -100,8 +101,8 @@ module Bootsnap
def test_extension_permutations def test_extension_permutations
cache = Cache.new(NullCache, [@dir1]) cache = Cache.new(NullCache, [@dir1])
assert_equal("#{@dir1}/dl#{DLEXT}", cache.find('dl')) assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("dl"))
refute(cache.find('dl', try_extensions: false)) refute(cache.find("dl", try_extensions: false))
assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("dl#{DLEXT}")) assert_equal("#{@dir1}/dl#{DLEXT}", cache.find("dl#{DLEXT}"))
assert_equal("#{@dir1}/both.rb", cache.find("both")) assert_equal("#{@dir1}/both.rb", cache.find("both"))
refute(cache.find("both", try_extensions: false)) refute(cache.find("both", try_extensions: false))
@ -114,14 +115,14 @@ module Bootsnap
def test_relative_paths_rescanned def test_relative_paths_rescanned
Dir.chdir(@dir2) do Dir.chdir(@dir2) do
cache = Cache.new(NullCache, %w(foo)) cache = Cache.new(NullCache, %w(foo))
refute(cache.find('bar/baz')) refute(cache.find("bar/baz"))
Dir.chdir(@dir1) do Dir.chdir(@dir1) do
# one caveat here is that you get the actual path back when # one caveat here is that you get the actual path back when
# resolving relative paths. On darwin, this means that # resolving relative paths. On darwin, this means that
# /var/folders/... comes back as /private/var/folders/... -- In # /var/folders/... comes back as /private/var/folders/... -- In
# production, this should be fine, but for this test to pass, we # production, this should be fine, but for this test to pass, we
# have to resolve it. # have to resolve it.
assert_equal(File.realpath("#{@dir1}/foo/bar/baz.rb"), cache.find('bar/baz')) assert_equal(File.realpath("#{@dir1}/foo/bar/baz.rb"), cache.find("bar/baz"))
end end
end end
end end
@ -136,14 +137,14 @@ module Bootsnap
FileUtils.touch("#{@dir1}/new.rb") FileUtils.touch("#{@dir1}/new.rb")
dev_no_cache.stubs(:now).returns(time + 31) dev_no_cache.stubs(:now).returns(time + 31)
refute(dev_no_cache.find('new')) refute(dev_no_cache.find("new"))
dev_yes_cache.stubs(:now).returns(time + 28) dev_yes_cache.stubs(:now).returns(time + 28)
assert_raises(Bootsnap::LoadPathCache::FallbackScan) do assert_raises(Bootsnap::LoadPathCache::FallbackScan) do
dev_yes_cache.find('new') dev_yes_cache.find("new")
end end
dev_yes_cache.stubs(:now).returns(time + 31) dev_yes_cache.stubs(:now).returns(time + 31)
assert(dev_yes_cache.find('new')) assert(dev_yes_cache.find("new"))
end end
def test_path_obj_equal? def test_path_obj_equal?
@ -152,35 +153,35 @@ module Bootsnap
path_obj.unshift(@dir1) path_obj.unshift(@dir1)
assert_equal("#{@dir1}/a.rb", cache.find('a')) assert_equal("#{@dir1}/a.rb", cache.find("a"))
end end
if RUBY_VERSION >= '2.5' && !(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/) if RUBY_VERSION >= "2.5" && RbConfig::CONFIG["host_os"] !~ /mswin|mingw|cygwin/
# https://github.com/ruby/ruby/pull/4061 # https://github.com/ruby/ruby/pull/4061
# https://bugs.ruby-lang.org/issues/17517 # https://bugs.ruby-lang.org/issues/17517
OS_ASCII_PATH_ENCODING = RUBY_VERSION >= '3.1' ? Encoding::UTF_8 : Encoding::US_ASCII OS_ASCII_PATH_ENCODING = RUBY_VERSION >= "3.1" ? Encoding::UTF_8 : Encoding::US_ASCII
def test_path_encoding def test_path_encoding
po = [@dir1] po = [@dir1]
cache = Cache.new(NullCache, po) cache = Cache.new(NullCache, po)
path = cache.find('a') path = cache.find("a")
assert_equal("#{@dir1}/a.rb", path) assert_equal("#{@dir1}/a.rb", path)
require path require path
internal_path = $LOADED_FEATURES.last internal_path = $LOADED_FEATURES.last
assert_equal(OS_ASCII_PATH_ENCODING, internal_path.encoding) assert_equal(OS_ASCII_PATH_ENCODING, internal_path.encoding)
assert_equal(OS_ASCII_PATH_ENCODING, path.encoding) assert_equal(OS_ASCII_PATH_ENCODING, path.encoding)
File.write(path, '') File.write(path, "")
assert_same path, internal_path assert_same path, internal_path
utf8_path = cache.find('béé') utf8_path = cache.find("béé")
require utf8_path require utf8_path
internal_utf8_path = $LOADED_FEATURES.last internal_utf8_path = $LOADED_FEATURES.last
assert_equal("#{@dir1}/béé.rb", utf8_path) assert_equal("#{@dir1}/béé.rb", utf8_path)
assert_equal(Encoding::UTF_8, internal_utf8_path.encoding) assert_equal(Encoding::UTF_8, internal_utf8_path.encoding)
assert_equal(Encoding::UTF_8, utf8_path.encoding) assert_equal(Encoding::UTF_8, utf8_path.encoding)
File.write(utf8_path, '') File.write(utf8_path, "")
assert_same utf8_path, internal_utf8_path assert_same utf8_path, internal_utf8_path
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -11,34 +12,34 @@ module Bootsnap
end end
def test_observes_changes def test_observes_changes
@observer.expects(:push_paths).with(@arr, 'a') @observer.expects(:push_paths).with(@arr, "a")
@arr << 'a' @arr << "a"
@observer.expects(:push_paths).with(@arr, 'b', 'c') @observer.expects(:push_paths).with(@arr, "b", "c")
@arr.push('b', 'c') @arr.push("b", "c")
@observer.expects(:push_paths).with(@arr, 'd', 'e') @observer.expects(:push_paths).with(@arr, "d", "e")
@arr.append('d', 'e') @arr.append("d", "e")
@observer.expects(:unshift_paths).with(@arr, 'f', 'g') @observer.expects(:unshift_paths).with(@arr, "f", "g")
@arr.unshift('f', 'g') @arr.unshift("f", "g")
@observer.expects(:push_paths).with(@arr, 'h', 'i') @observer.expects(:push_paths).with(@arr, "h", "i")
@arr.concat(%w(h i)) @arr.concat(%w(h i))
@observer.expects(:unshift_paths).with(@arr, 'j', 'k') @observer.expects(:unshift_paths).with(@arr, "j", "k")
@arr.prepend('j', 'k') @arr.prepend("j", "k")
end end
def test_reinitializes_on_aggressive_modifications def test_reinitializes_on_aggressive_modifications
@observer.expects(:push_paths).with(@arr, 'a', 'b', 'c') @observer.expects(:push_paths).with(@arr, "a", "b", "c")
@arr.push('a', 'b', 'c') @arr.push("a", "b", "c")
@observer.expects(:reinitialize).times(4) @observer.expects(:reinitialize).times(4)
@arr.delete(3) @arr.delete(3)
@arr.compact! @arr.compact!
@arr.map!(&:upcase) @arr.map!(&:upcase)
assert_equal('C', @arr.pop) assert_equal("C", @arr.pop)
assert_equal(%w(A B), @arr) assert_equal(%w(A B), @arr)
end end
@ -50,8 +51,8 @@ module Bootsnap
def test_register_twice_observes_once def test_register_twice_observes_once
ChangeObserver.register(@observer, @arr) ChangeObserver.register(@observer, @arr)
@observer.expects(:push_paths).with(@arr, 'a').once @observer.expects(:push_paths).with(@arr, "a").once
@arr << 'a' @arr << "a"
assert_equal(%w(a), @arr) assert_equal(%w(a), @arr)
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
module Bootsnap module Bootsnap
module KernelRequireTest module KernelRequireTest
@ -24,10 +25,10 @@ module Bootsnap
end end
def test_no_exstensions_for_kernel_load def test_no_exstensions_for_kernel_load
assert_raises(LoadError) { load 'a' } assert_raises(LoadError) { load "a" }
assert(load 'no_ext') assert(load("no_ext"))
Dir.chdir(@dir2) Dir.chdir(@dir2)
assert(load 'loads.rb') assert(load("loads.rb"))
end end
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -11,35 +12,35 @@ module Bootsnap
end end
def test_successful_addition def test_successful_addition
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('bundler', '/a/b/bundler.rb') {} @index.register("bundler", "/a/b/bundler.rb") {}
assert(@index.key?('bundler')) assert(@index.key?("bundler"))
assert(@index.key?('bundler.rb')) assert(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_no_add_on_raise def test_no_add_on_raise
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
assert_raises(RuntimeError) do assert_raises(RuntimeError) do
@index.register('bundler', '/a/b/bundler.rb') { raise } @index.register("bundler", "/a/b/bundler.rb") { raise }
end end
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_infer_base_from_ext def test_infer_base_from_ext
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('bundler.rb') {} @index.register("bundler.rb") {}
assert(@index.key?('bundler')) assert(@index.key?("bundler"))
assert(@index.key?('bundler.rb')) assert(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_only_strip_elidable_ext def test_only_strip_elidable_ext
@ -48,95 +49,95 @@ module Bootsnap
# #
# E.g. 'descriptor.pb.rb' if required via 'descriptor.pb' # E.g. 'descriptor.pb.rb' if required via 'descriptor.pb'
# should never be shortened to merely 'descriptor'! # should never be shortened to merely 'descriptor'!
refute(@index.key?('descriptor.pb')) refute(@index.key?("descriptor.pb"))
refute(@index.key?('descriptor.pb.rb')) refute(@index.key?("descriptor.pb.rb"))
refute(@index.key?('descriptor.rb')) refute(@index.key?("descriptor.rb"))
refute(@index.key?('descriptor')) refute(@index.key?("descriptor"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('descriptor.pb.rb') {} @index.register("descriptor.pb.rb") {}
assert(@index.key?('descriptor.pb')) assert(@index.key?("descriptor.pb"))
assert(@index.key?('descriptor.pb.rb')) assert(@index.key?("descriptor.pb.rb"))
refute(@index.key?('descriptor.rb')) refute(@index.key?("descriptor.rb"))
refute(@index.key?('descriptor')) refute(@index.key?("descriptor"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_shared_library_ext_considered_elidable def test_shared_library_ext_considered_elidable
# Check that '.dylib' (token shared library extension) is treated as elidable, # Check that '.dylib' (token shared library extension) is treated as elidable,
# and doesn't get mixed up with Ruby '.rb' files. # and doesn't get mixed up with Ruby '.rb' files.
refute(@index.key?('libgit2.dylib')) refute(@index.key?("libgit2.dylib"))
refute(@index.key?('libgit2.dylib.rb')) refute(@index.key?("libgit2.dylib.rb"))
refute(@index.key?('descriptor.rb')) refute(@index.key?("descriptor.rb"))
refute(@index.key?('descriptor')) refute(@index.key?("descriptor"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('libgit2.dylib') {} @index.register("libgit2.dylib") {}
assert(@index.key?('libgit2.dylib')) assert(@index.key?("libgit2.dylib"))
refute(@index.key?('libgit2.dylib.rb')) refute(@index.key?("libgit2.dylib.rb"))
refute(@index.key?('libgit2.rb')) refute(@index.key?("libgit2.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_cannot_infer_ext_from_base # Current limitation def test_cannot_infer_ext_from_base # Current limitation
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('bundler') {} @index.register("bundler") {}
assert(@index.key?('bundler')) assert(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_purge_loaded_feature def test_purge_loaded_feature
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('bundler', '/a/b/bundler.rb') {} @index.register("bundler", "/a/b/bundler.rb") {}
assert(@index.key?('bundler')) assert(@index.key?("bundler"))
assert(@index.key?('bundler.rb')) assert(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.purge('/a/b/bundler.rb') @index.purge("/a/b/bundler.rb")
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_purge_multi_loaded_feature def test_purge_multi_loaded_feature
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('bundler', '/a/b/bundler.rb') {} @index.register("bundler", "/a/b/bundler.rb") {}
assert(@index.key?('bundler')) assert(@index.key?("bundler"))
assert(@index.key?('bundler.rb')) assert(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.purge_multi(['/a/b/bundler.rb', '/a/b/does-not-exist.rb']) @index.purge_multi(["/a/b/bundler.rb", "/a/b/does-not-exist.rb"])
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_register_finds_correct_feature def test_register_finds_correct_feature
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.register('bundler', nil) { $LOADED_FEATURES << '/a/b/bundler.rb' } @index.register("bundler", nil) { $LOADED_FEATURES << "/a/b/bundler.rb" }
assert(@index.key?('bundler')) assert(@index.key?("bundler"))
assert(@index.key?('bundler.rb')) assert(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
@index.purge('/a/b/bundler.rb') @index.purge("/a/b/bundler.rb")
refute(@index.key?('bundler')) refute(@index.key?("bundler"))
refute(@index.key?('bundler.rb')) refute(@index.key?("bundler.rb"))
refute(@index.key?('foo')) refute(@index.key?("foo"))
end end
def test_derives_initial_state_from_loaded_features def test_derives_initial_state_from_loaded_features
index = LoadedFeaturesIndex.new index = LoadedFeaturesIndex.new
assert(index.key?('minitest/autorun')) assert(index.key?("minitest/autorun"))
assert(index.key?('minitest/autorun.rb')) assert(index.key?("minitest/autorun.rb"))
refute(index.key?('minitest/autorun.so')) refute(index.key?("minitest/autorun.so"))
end end
def test_works_with_pathname def test_works_with_pathname
path = 'bundler.rb' path = "bundler.rb"
pathname = Pathname.new(path) pathname = Pathname.new(path)
@index.register(pathname, path) { true } @index.register(pathname, path) { true }
assert(@index.key?(pathname)) assert(@index.key?(pathname))

View File

@ -1,11 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
class PathScannerTest < MiniTest::Test class PathScannerTest < MiniTest::Test
DLEXT = RbConfig::CONFIG['DLEXT'] DLEXT = RbConfig::CONFIG["DLEXT"]
OTHER_DLEXT = DLEXT == 'bundle' ? 'so' : 'bundle' OTHER_DLEXT = DLEXT == "bundle" ? "so" : "bundle"
def test_scans_requirables_and_dirs def test_scans_requirables_and_dirs
Dir.mktmpdir do |dir| Dir.mktmpdir do |dir|

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require('bootsnap/load_path_cache') require("test_helper")
require("bootsnap/load_path_cache")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -10,17 +11,17 @@ module Bootsnap
end end
def test_stability def test_stability
require('time') require("time")
time_file = Time.method(:rfc2822).source_location[0] time_file = Time.method(:rfc2822).source_location[0]
volatile = Path.new(__FILE__) volatile = Path.new(__FILE__)
stable = Path.new(time_file) stable = Path.new(time_file)
unknown = Path.new('/who/knows') unknown = Path.new("/who/knows")
lib = Path.new(RbConfig::CONFIG['libdir'] + '/a') lib = Path.new(RbConfig::CONFIG["libdir"] + "/a")
site = Path.new(RbConfig::CONFIG['sitedir'] + '/b') site = Path.new(RbConfig::CONFIG["sitedir"] + "/b")
absolute_prefix = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ ? ENV['SystemDrive'] : '' absolute_prefix = RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/ ? ENV["SystemDrive"] : ""
bundler = Path.new(absolute_prefix + '/bp/3') bundler = Path.new(absolute_prefix + "/bp/3")
Bundler.stubs(:bundle_path).returns(absolute_prefix + '/bp') Bundler.stubs(:bundle_path).returns(absolute_prefix + "/bp")
assert(stable.stable?, "The stable path #{stable.path.inspect} was unexpectedly not stable.") assert(stable.stable?, "The stable path #{stable.path.inspect} was unexpectedly not stable.")
refute(stable.volatile?, "The stable path #{stable.path.inspect} was unexpectedly volatile.") refute(stable.volatile?, "The stable path #{stable.path.inspect} was unexpectedly volatile.")
@ -35,17 +36,17 @@ module Bootsnap
end end
def test_non_directory? def test_non_directory?
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
refute(Path.new('c:/dev').non_directory?) refute(Path.new("c:/dev").non_directory?)
refute(Path.new('c:/nope').non_directory?) refute(Path.new("c:/nope").non_directory?)
# there isn't a direct analog i could think of # there isn't a direct analog i could think of
# assert(Path.new('/dev/null').non_directory?) # assert(Path.new('/dev/null').non_directory?)
assert(Path.new("#{ENV['WinDir']}/System32/Drivers/Etc/hosts").non_directory?) assert(Path.new("#{ENV['WinDir']}/System32/Drivers/Etc/hosts").non_directory?)
else else
refute(Path.new('/dev').non_directory?) refute(Path.new("/dev").non_directory?)
refute(Path.new('/nope').non_directory?) refute(Path.new("/nope").non_directory?)
assert(Path.new('/dev/null').non_directory?) assert(Path.new("/dev/null").non_directory?)
assert(Path.new('/etc/hosts').non_directory?) assert(Path.new("/etc/hosts").non_directory?)
end end
end end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper') require("test_helper")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
class RealpathCacheTest < MiniTest::Test class RealpathCacheTest < MiniTest::Test
EXTENSIONS = ['', *CACHED_EXTENSIONS] EXTENSIONS = ["", *CACHED_EXTENSIONS].freeze
def setup def setup
@cache = RealpathCache.new @cache = RealpathCache.new
@ -16,13 +16,13 @@ module Bootsnap
@symlinked_dir = "#{@base_dir}/symlink" @symlinked_dir = "#{@base_dir}/symlink"
FileUtils.ln_s(@absolute_dir, @symlinked_dir) FileUtils.ln_s(@absolute_dir, @symlinked_dir)
real_caller = File.new("#{@absolute_dir}/real_caller.rb", 'w').tap(&:close).path real_caller = File.new("#{@absolute_dir}/real_caller.rb", "w").tap(&:close).path
symlinked_caller = "#{@absolute_dir}/symlinked_caller.rb" symlinked_caller = "#{@absolute_dir}/symlinked_caller.rb"
FileUtils.ln_s(real_caller, symlinked_caller) FileUtils.ln_s(real_caller, symlinked_caller)
EXTENSIONS.each do |ext| EXTENSIONS.each do |ext|
real_required = File.new("#{@absolute_dir}/real_required#{ext}", 'w').tap(&:close).path real_required = File.new("#{@absolute_dir}/real_required#{ext}", "w").tap(&:close).path
symlinked_required = "#{@absolute_dir}/symlinked_required#{ext}" symlinked_required = "#{@absolute_dir}/symlinked_required#{ext}"
FileUtils.ln_s(real_required, symlinked_required) FileUtils.ln_s(real_required, symlinked_required)
@ -40,9 +40,11 @@ module Bootsnap
end end
end end
variants = %w(absolute symlink).product(%w(absolute symlink), variants = %w(absolute symlink).product(
%w(absolute symlink),
%w(real_caller symlinked_caller), %w(real_caller symlinked_caller),
%w(real_required symlinked_required)) %w(real_required symlinked_required),
)
variants.each do |caller_dir, required_dir, caller_file, required_file| variants.each do |caller_dir, required_dir, caller_file, required_file|
method_name = "test_with_#{caller_dir}_caller_dir_" \ method_name = "test_with_#{caller_dir}_caller_dir_" \

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require('tmpdir') require("test_helper")
require('fileutils') require("tmpdir")
require("fileutils")
module Bootsnap module Bootsnap
module LoadPathCache module LoadPathCache
@ -19,36 +20,36 @@ module Bootsnap
attr_reader(:store) attr_reader(:store)
def test_persistence def test_persistence
store.transaction { store.set('a', 'b') } store.transaction { store.set("a", "b") }
store2 = Store.new(@path) store2 = Store.new(@path)
assert_equal('b', store2.get('a')) assert_equal("b", store2.get("a"))
end end
def test_modification def test_modification
store.transaction { store.set('a', 'b') } store.transaction { store.set("a", "b") }
store2 = Store.new(@path) store2 = Store.new(@path)
assert_equal('b', store2.get('a')) assert_equal("b", store2.get("a"))
store.transaction { store.set('a', 'c') } store.transaction { store.set("a", "c") }
store3 = Store.new(@path) store3 = Store.new(@path)
assert_equal('c', store3.get('a')) assert_equal("c", store3.get("a"))
end end
def test_stores_arrays def test_stores_arrays
store.transaction { store.set('a', [1234, %w(a b)]) } store.transaction { store.set("a", [1234, %w(a b)]) }
store2 = Store.new(@path) store2 = Store.new(@path)
assert_equal([1234, %w(a b)], store2.get('a')) assert_equal([1234, %w(a b)], store2.get("a"))
end end
def test_transaction_required_to_set def test_transaction_required_to_set
assert_raises(Store::SetOutsideTransactionNotAllowed) do assert_raises(Store::SetOutsideTransactionNotAllowed) do
store.set('a', 'b') store.set("a", "b")
end end
assert_raises(Store::SetOutsideTransactionNotAllowed) do assert_raises(Store::SetOutsideTransactionNotAllowed) do
store.fetch('a') { 'b' } store.fetch("a") { "b" }
end end
end end
@ -59,35 +60,35 @@ module Bootsnap
end end
def test_no_commit_unless_dirty def test_no_commit_unless_dirty
store.transaction { store.set('a', nil) } store.transaction { store.set("a", nil) }
refute(File.exist?(@path)) refute(File.exist?(@path))
store.transaction { store.set('a', 1) } store.transaction { store.set("a", 1) }
assert(File.exist?(@path)) assert(File.exist?(@path))
end end
def test_retry_on_collision def test_retry_on_collision
retries = sequence('retries') retries = sequence("retries")
MessagePack.expects(:dump).in_sequence(retries).raises(Errno::EEXIST.new("File exists @ rb_sysopen")) MessagePack.expects(:dump).in_sequence(retries).raises(Errno::EEXIST.new("File exists @ rb_sysopen"))
MessagePack.expects(:dump).in_sequence(retries).returns(1) MessagePack.expects(:dump).in_sequence(retries).returns(1)
FileUtils.expects(:mv).in_sequence(retries) FileUtils.expects(:mv).in_sequence(retries)
store.transaction { store.set('a', 1) } store.transaction { store.set("a", 1) }
end end
def test_ignore_read_only_filesystem def test_ignore_read_only_filesystem
MessagePack.expects(:dump).raises(Errno::EROFS.new("Read-only file system @ rb_sysopen")) MessagePack.expects(:dump).raises(Errno::EROFS.new("Read-only file system @ rb_sysopen"))
store.transaction { store.set('a', 1) } store.transaction { store.set("a", 1) }
refute(File.exist?(@path)) refute(File.exist?(@path))
end end
def test_bust_cache_on_ruby_change def test_bust_cache_on_ruby_change
store.transaction { store.set('a', 'b') } store.transaction { store.set("a", "b") }
assert_equal 'b', Store.new(@path).get('a') assert_equal "b", Store.new(@path).get("a")
stub_const(Store, :CURRENT_VERSION, "foobar") do stub_const(Store, :CURRENT_VERSION, "foobar") do
assert_nil Store.new(@path).get('a') assert_nil Store.new(@path).get("a")
end end
end end

View File

@ -1,3 +1,4 @@
# frozen_string_literal: true # frozen_string_literal: true
require('bundler/setup')
require('bootsnap/setup') require("bundler/setup")
require("bootsnap/setup")

View File

@ -1,12 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require("test_helper")
module Bootsnap module Bootsnap
class SetupTest < Minitest::Test class SetupTest < Minitest::Test
def setup def setup
@_old_env = ENV.to_h @_old_env = ENV.to_h
@tmp_dir = Dir.mktmpdir('bootsnap-test') @tmp_dir = Dir.mktmpdir("bootsnap-test")
ENV['BOOTSNAP_CACHE_DIR'] = @tmp_dir ENV["BOOTSNAP_CACHE_DIR"] = @tmp_dir
end end
def teardown def teardown
@ -27,7 +28,7 @@ module Bootsnap
end end
def test_default_setup_with_ENV_not_dev def test_default_setup_with_ENV_not_dev
ENV['ENV'] = 'something' ENV["ENV"] = "something"
Bootsnap.expects(:setup).with( Bootsnap.expects(:setup).with(
cache_dir: @tmp_dir, cache_dir: @tmp_dir,
@ -42,7 +43,7 @@ module Bootsnap
end end
def test_default_setup_with_DISABLE_BOOTSNAP_LOAD_PATH_CACHE def test_default_setup_with_DISABLE_BOOTSNAP_LOAD_PATH_CACHE
ENV['DISABLE_BOOTSNAP_LOAD_PATH_CACHE'] = 'something' ENV["DISABLE_BOOTSNAP_LOAD_PATH_CACHE"] = "something"
Bootsnap.expects(:setup).with( Bootsnap.expects(:setup).with(
cache_dir: @tmp_dir, cache_dir: @tmp_dir,
@ -57,7 +58,7 @@ module Bootsnap
end end
def test_default_setup_with_DISABLE_BOOTSNAP_COMPILE_CACHE def test_default_setup_with_DISABLE_BOOTSNAP_COMPILE_CACHE
ENV['DISABLE_BOOTSNAP_COMPILE_CACHE'] = 'something' ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"] = "something"
Bootsnap.expects(:setup).with( Bootsnap.expects(:setup).with(
cache_dir: @tmp_dir, cache_dir: @tmp_dir,
@ -72,14 +73,14 @@ module Bootsnap
end end
def test_default_setup_with_DISABLE_BOOTSNAP def test_default_setup_with_DISABLE_BOOTSNAP
ENV['DISABLE_BOOTSNAP'] = 'something' ENV["DISABLE_BOOTSNAP"] = "something"
Bootsnap.expects(:setup).never Bootsnap.expects(:setup).never
Bootsnap.default_setup Bootsnap.default_setup
end end
def test_default_setup_with_BOOTSNAP_LOG def test_default_setup_with_BOOTSNAP_LOG
ENV['BOOTSNAP_LOG'] = 'something' ENV["BOOTSNAP_LOG"] = "something"
Bootsnap.expects(:setup).with( Bootsnap.expects(:setup).with(
cache_dir: @tmp_dir, cache_dir: @tmp_dir,

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
if defined? Warning if defined? Warning
if Warning.respond_to?(:[]=) if Warning.respond_to?(:[]=)
@ -7,18 +8,18 @@ if defined? Warning
end end
end end
require('bundler/setup') require("bundler/setup")
require('bootsnap') require("bootsnap")
require('bootsnap/compile_cache/yaml') require("bootsnap/compile_cache/yaml")
require('bootsnap/compile_cache/json') require("bootsnap/compile_cache/json")
require('tmpdir') require("tmpdir")
require('fileutils') require("fileutils")
require('minitest/autorun') require("minitest/autorun")
require('mocha/minitest') require("mocha/minitest")
cache_dir = File.expand_path('../../tmp/bootsnap/compile-cache', __FILE__) cache_dir = File.expand_path("../tmp/bootsnap/compile-cache", __dir__)
Bootsnap::CompileCache.setup(cache_dir: cache_dir, iseq: true, yaml: false, json: false) Bootsnap::CompileCache.setup(cache_dir: cache_dir, iseq: true, yaml: false, json: false)
if GC.respond_to?(:verify_compaction_references) if GC.respond_to?(:verify_compaction_references)
@ -28,16 +29,16 @@ if GC.respond_to?(:verify_compaction_references)
end end
module TestHandler module TestHandler
def self.input_to_storage(_i, p) def self.input_to_storage(_input, path)
'neato ' + p "neato " + path
end end
def self.storage_to_output(d, _a) def self.storage_to_output(data, _kwargs)
d.upcase data.upcase
end end
def self.input_to_output(_d, _a) def self.input_to_output(_data, _kwargs)
raise('but why tho') raise("but why tho")
end end
end end
@ -67,7 +68,7 @@ module MiniTest
hash ^= fnv1a_64(args_key) hash ^= fnv1a_64(args_key)
end end
hex = hash.to_s(16).rjust(16, '0') hex = hash.to_s(16).rjust(16, "0")
"#{dir}/#{hex[0..1]}/#{hex[2..-1]}" "#{dir}/#{hex[0..1]}/#{hex[2..-1]}"
end end
@ -95,7 +96,7 @@ module TmpdirHelper
def setup def setup
super super
@prev_dir = Dir.pwd @prev_dir = Dir.pwd
@tmp_dir = Dir.mktmpdir('bootsnap-test') @tmp_dir = Dir.mktmpdir("bootsnap-test")
Dir.chdir(@tmp_dir) Dir.chdir(@tmp_dir)
@prev = Bootsnap::CompileCache::ISeq.cache_dir @prev = Bootsnap::CompileCache::ISeq.cache_dir
Bootsnap::CompileCache::ISeq.cache_dir = @tmp_dir Bootsnap::CompileCache::ISeq.cache_dir = @tmp_dir

View File

@ -1,20 +1,21 @@
# frozen_string_literal: true # frozen_string_literal: true
require('test_helper')
require('bootsnap/cli') require("test_helper")
require("bootsnap/cli")
module Bootsnap module Bootsnap
class WorkerPoolTestTest < Minitest::Test class WorkerPoolTestTest < Minitest::Test
def test_dispatch def test_dispatch
@pool = CLI::WorkerPool.create(size: 2, jobs: { touch: ->(path) { File.write(path, $$.to_s) } }) @pool = CLI::WorkerPool.create(size: 2, jobs: {touch: ->(path) { File.write(path, Process.pid.to_s) }})
@pool.spawn @pool.spawn
Dir.mktmpdir('bootsnap-test') do |tmpdir| Dir.mktmpdir("bootsnap-test") do |tmpdir|
10.times do |i| 10.times do |i|
@pool.push(:touch, File.join(tmpdir, i.to_s)) @pool.push(:touch, File.join(tmpdir, i.to_s))
end end
@pool.shutdown @pool.shutdown
files = Dir.chdir(tmpdir) { Dir['*'] }.sort files = Dir.chdir(tmpdir) { Dir["*"] }.sort
assert_equal 10.times.map(&:to_s), files assert_equal 10.times.map(&:to_s), files
end end
end end