summaryrefslogtreecommitdiff
path: root/spec/syntax_suggest/integration
diff options
context:
space:
mode:
Diffstat (limited to 'spec/syntax_suggest/integration')
-rw-r--r--spec/syntax_suggest/integration/exe_cli_spec.rb27
-rw-r--r--spec/syntax_suggest/integration/ruby_command_line_spec.rb193
-rw-r--r--spec/syntax_suggest/integration/syntax_suggest_spec.rb239
3 files changed, 459 insertions, 0 deletions
diff --git a/spec/syntax_suggest/integration/exe_cli_spec.rb b/spec/syntax_suggest/integration/exe_cli_spec.rb
new file mode 100644
index 0000000000..b9a3173715
--- /dev/null
+++ b/spec/syntax_suggest/integration/exe_cli_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require_relative "../spec_helper"
+
+module SyntaxSuggest
+ RSpec.describe "exe" do
+ def exe_path
+ if ruby_core?
+ root_dir.join("../libexec").join("syntax_suggest")
+ else
+ root_dir.join("exe").join("syntax_suggest")
+ end
+ end
+
+ def exe(cmd)
+ ruby = ENV.fetch("RUBY", "ruby")
+ out = run!("#{ruby} #{exe_path} #{cmd}", raise_on_nonzero_exit: false)
+ puts out if ENV["SYNTAX_SUGGEST_DEBUG"]
+ out
+ end
+
+ it "prints the version" do
+ out = exe("-v")
+ expect(out.strip).to include(SyntaxSuggest::VERSION)
+ end
+ end
+end
diff --git a/spec/syntax_suggest/integration/ruby_command_line_spec.rb b/spec/syntax_suggest/integration/ruby_command_line_spec.rb
new file mode 100644
index 0000000000..c1ec4be54e
--- /dev/null
+++ b/spec/syntax_suggest/integration/ruby_command_line_spec.rb
@@ -0,0 +1,193 @@
+# frozen_string_literal: true
+
+require_relative "../spec_helper"
+
+module SyntaxSuggest
+ ruby = ENV.fetch("RUBY", "ruby")
+ RSpec.describe "Requires with ruby cli" do
+ it "namespaces all monkeypatched methods" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ puts Kernel.private_methods
+ EOM
+
+ syntax_suggest_methods_file = tmpdir.join("syntax_suggest_methods.txt")
+ api_only_methods_file = tmpdir.join("api_only_methods.txt")
+ kernel_methods_file = tmpdir.join("kernel_methods.txt")
+
+ d_pid = Process.spawn("#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1 > #{syntax_suggest_methods_file}")
+ k_pid = Process.spawn("#{ruby} #{script} 2>&1 >> #{kernel_methods_file}")
+ r_pid = Process.spawn("#{ruby} -I#{lib_dir} -rsyntax_suggest/api #{script} 2>&1 > #{api_only_methods_file}")
+
+ Process.wait(k_pid)
+ Process.wait(d_pid)
+ Process.wait(r_pid)
+
+ kernel_methods_array = kernel_methods_file.read.strip.lines.map(&:strip)
+ syntax_suggest_methods_array = syntax_suggest_methods_file.read.strip.lines.map(&:strip)
+ api_only_methods_array = api_only_methods_file.read.strip.lines.map(&:strip)
+
+ # In ruby 3.1.0-preview1 the `timeout` file is already required
+ # we can remove it if it exists to normalize the output for
+ # all ruby versions
+ [syntax_suggest_methods_array, kernel_methods_array, api_only_methods_array].each do |array|
+ array.delete("timeout")
+ end
+
+ methods = (syntax_suggest_methods_array - kernel_methods_array).sort
+ if methods.any?
+ expect(methods).to eq(["syntax_suggest_original_load", "syntax_suggest_original_require", "syntax_suggest_original_require_relative"])
+ end
+
+ methods = (api_only_methods_array - kernel_methods_array).sort
+ expect(methods).to eq([])
+ end
+ end
+
+ # Since Ruby 3.2 includes syntax_suggest as a default gem, we might accidentally
+ # be requiring the default gem instead of this library under test. Assert that's
+ # not the case
+ it "tests current version of syntax_suggest" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ contents = <<~'EOM'
+ puts "suggest_version is #{SyntaxSuggest::VERSION}"
+ EOM
+ script.write(contents)
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest/version #{script} 2>&1`
+
+ expect(out).to include("suggest_version is #{SyntaxSuggest::VERSION}").once
+ end
+ end
+
+ it "detects require error and adds a message with auto mode" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ describe "things" do
+ it "blerg" do
+ end
+
+ it "flerg"
+ end
+
+ it "zlerg" do
+ end
+ end
+ EOM
+
+ require_rb = tmpdir.join("require.rb")
+ require_rb.write <<~EOM
+ load "#{script.expand_path}"
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{require_rb} 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out).to include('> 5 it "flerg"').once
+ end
+ end
+
+ it "gem can be tested when executing on Ruby with default gem included" do
+ skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2")
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest -e "puts SyntaxError.instance_method(:detailed_message).source_location" 2>&1`
+
+ expect($?.success?).to be_truthy
+ expect(out).to include(lib_dir.join("syntax_suggest").join("core_ext.rb").to_s).once
+ end
+
+ it "annotates a syntax error in Ruby 3.2+ when require is not used" do
+ skip if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.2")
+
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ describe "things" do
+ it "blerg" do
+ end
+
+ it "flerg"
+ end
+
+ it "zlerg" do
+ end
+ end
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out).to include('> 5 it "flerg"').once
+ end
+ end
+
+ it "does not load internals into memory if no syntax error" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ class Dog
+ end
+
+ if defined?(SyntaxSuggest::DEFAULT_VALUE)
+ puts "SyntaxSuggest is loaded"
+ else
+ puts "SyntaxSuggest is NOT loaded"
+ end
+ EOM
+
+ require_rb = tmpdir.join("require.rb")
+ require_rb.write <<~EOM
+ load "#{script.expand_path}"
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{require_rb} 2>&1`
+
+ expect($?.success?).to be_truthy
+ expect(out).to include("SyntaxSuggest is NOT loaded").once
+ end
+ end
+
+ it "ignores eval" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ $stderr = STDOUT
+ eval("def lol")
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest #{script} 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out).to match(/\(eval.*\):1/)
+
+ expect(out).to_not include("SyntaxSuggest")
+ expect(out).to_not include("Could not find filename")
+ end
+ end
+
+ it "does not say 'syntax ok' when a syntax error fires" do
+ Dir.mktmpdir do |dir|
+ tmpdir = Pathname(dir)
+ script = tmpdir.join("script.rb")
+ script.write <<~EOM
+ break
+ EOM
+
+ out = `#{ruby} -I#{lib_dir} -rsyntax_suggest -e "require_relative '#{script}'" 2>&1`
+
+ expect($?.success?).to be_falsey
+ expect(out.downcase).to_not include("syntax ok")
+ expect(out).to include("Invalid break")
+ end
+ end
+ end
+end
diff --git a/spec/syntax_suggest/integration/syntax_suggest_spec.rb b/spec/syntax_suggest/integration/syntax_suggest_spec.rb
new file mode 100644
index 0000000000..9071d37c1b
--- /dev/null
+++ b/spec/syntax_suggest/integration/syntax_suggest_spec.rb
@@ -0,0 +1,239 @@
+# frozen_string_literal: true
+
+require_relative "../spec_helper"
+
+module SyntaxSuggest
+ RSpec.describe "Integration tests that don't spawn a process (like using the cli)" do
+ it "does not timeout on massive files" do
+ next unless ENV["SYNTAX_SUGGEST_TIMEOUT"]
+
+ file = fixtures_dir.join("syntax_tree.rb.txt")
+ lines = file.read.lines
+ lines.delete_at(768 - 1)
+
+ io = StringIO.new
+
+ benchmark = Benchmark.measure do
+ debug_perf do
+ SyntaxSuggest.call(
+ io: io,
+ source: lines.join,
+ filename: file
+ )
+ end
+ end
+
+ debug_display(io.string)
+ debug_display(benchmark)
+
+ expect(io.string).to include(<<~EOM)
+ 6 class SyntaxTree < Ripper
+ 170 def self.parse(source)
+ 174 end
+ > 754 def on_args_add(arguments, argument)
+ > 776 class ArgsAddBlock
+ > 810 end
+ 9233 end
+ EOM
+ end
+
+ it "re-checks all block code, not just what's visible issues/95" do
+ file = fixtures_dir.join("ruby_buildpack.rb.txt")
+ io = StringIO.new
+
+ debug_perf do
+ benchmark = Benchmark.measure do
+ SyntaxSuggest.call(
+ io: io,
+ source: file.read,
+ filename: file
+ )
+ end
+ debug_display(io.string)
+ debug_display(benchmark)
+ end
+
+ expect(io.string).to_not include("def ruby_install_binstub_path")
+ expect(io.string).to include(<<~EOM)
+ > 1067 def add_yarn_binary
+ > 1068 return [] if yarn_preinstalled?
+ > 1069 |
+ > 1075 end
+ EOM
+ end
+
+ it "returns good results on routes.rb" do
+ source = fixtures_dir.join("routes.rb.txt").read
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ debug_display(io.string)
+
+ expect(io.string).to include(<<~EOM)
+ 1 Rails.application.routes.draw do
+ > 113 namespace :admin do
+ > 116 match "/foobar(*path)", via: :all, to: redirect { |_params, req|
+ > 120 }
+ 121 end
+ EOM
+ end
+
+ it "handles multi-line-methods issues/64" do
+ source = fixtures_dir.join("webmock.rb.txt").read
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ debug_display(io.string)
+
+ expect(io.string).to include(<<~EOM)
+ 1 describe "webmock tests" do
+ 22 it "body" do
+ 27 query = Cutlass::FunctionQuery.new(
+ > 28 port: port
+ > 29 body: body
+ 30 ).call
+ 34 end
+ 35 end
+ EOM
+ end
+
+ it "handles derailed output issues/50" do
+ source = fixtures_dir.join("derailed_require_tree.rb.txt").read
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ debug_display(io.string)
+
+ expect(io.string).to include(<<~EOM)
+ 5 module DerailedBenchmarks
+ 6 class RequireTree
+ > 13 def initialize(name)
+ > 18 def self.reset!
+ > 25 end
+ 73 end
+ 74 end
+ EOM
+ end
+
+ it "handles heredocs" do
+ lines = fixtures_dir.join("rexe.rb.txt").read.lines
+ lines.delete_at(85 - 1)
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: lines.join
+ )
+
+ out = io.string
+ debug_display(out)
+
+ expect(out).to include(<<~EOM)
+ 16 class Rexe
+ > 77 class Lookups
+ > 78 def input_modes
+ > 148 end
+ 551 end
+ EOM
+ end
+
+ it "rexe" do
+ lines = fixtures_dir.join("rexe.rb.txt").read.lines
+ lines.delete_at(148 - 1)
+ source = lines.join
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ 16 class Rexe
+ > 77 class Lookups
+ > 140 def format_requires
+ > 148 end
+ 551 end
+ EOM
+ end
+
+ it "ambiguous end" do
+ source = <<~EOM
+ def call # 0
+ print "lol" # 1
+ end # one # 2
+ end # two # 3
+ EOM
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ > 1 def call # 0
+ > 3 end # one # 2
+ > 4 end # two # 3
+ EOM
+ end
+
+ it "simple regression" do
+ source = <<~EOM
+ class Dog
+ def bark
+ puts "woof"
+ end
+ EOM
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ > 1 class Dog
+ > 2 def bark
+ > 4 end
+ EOM
+ end
+
+ it "empty else" do
+ source = <<~EOM
+ class Foo
+ def foo
+ if cond?
+ foo
+ else
+
+ end
+ end
+
+ # ...
+
+ def bar
+ if @recv
+ end_is_missing_here
+ end
+ end
+ EOM
+
+ io = StringIO.new
+ SyntaxSuggest.call(
+ io: io,
+ source: source
+ )
+ out = io.string
+ expect(out).to include(<<~EOM)
+ end_is_missing_here
+ EOM
+ end
+ end
+end