Rewrite cd tests to drive pry instance directly

This commit is contained in:
Ryan Fitzgerald 2012-07-23 04:03:05 -04:00
parent 6fd797d302
commit 62a684b9d9
5 changed files with 166 additions and 210 deletions

View File

@ -66,9 +66,16 @@ class Pry
# Restore old stack to its initial values.
state.old_stack = old_stack
output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
output.puts e.inspect
return
msg = [
"Bad object path: #{arg_string}.",
"Failed trying to resolve: #{context}.",
e.inspect
].join(' ')
CommandError.new(msg).tap do |err|
err.set_backtrace e.backtrace
raise err
end
end
end

View File

@ -196,7 +196,6 @@ class Pry
exec_hook :before_session, output, target, self
set_last_result(nil, target)
@input_array << nil # add empty input so _in_ and _out_ match
binding_stack.push target
@ -272,10 +271,7 @@ class Pry
exec_hook :before_eval, code, self
result = target.eval(code, Pry.eval_path, Pry.current_line)
set_last_result(result, target, code)
result
result = evaluate_ruby(code, target)
rescue RescuableException => e
self.last_exception = e
e
@ -320,6 +316,15 @@ class Pry
eval_string
end
def evaluate_ruby(code, target = binding_stack.last)
target = Pry.binding_for(target)
inject_sticky_locals(target)
target.eval(code, Pry.eval_path, Pry.current_line).tap do |result|
set_last_result(result, target, code)
end
end
# Output the result or pass to an exception handler (if result is an exception).
def show_result(result)
if last_result_is_exception?
@ -419,7 +424,7 @@ class Pry
# @param [String] eval_string The cumulative lines of input.
# @param [Binding] target The target of the Pry session.
# @return [Boolean] `true` if `val` is a command, `false` otherwise
def process_command(val, eval_string, target)
def process_command(val, eval_string = '', target = TOPLEVEL_BINDING)
result = commands.process_line(val, {
:target => target,
:output => output,

View File

@ -125,7 +125,6 @@ def redirect_pry_io(new_in, new_out = StringIO.new)
end
def mock_pry(*args)
args.flatten!
binding = args.first.is_a?(Binding) ? args.shift : binding()

View File

@ -1,290 +1,248 @@
require 'helper'
module CdTestHelpers
def binding_stack
evaluate_ruby '_pry_.binding_stack.dup'
end
def assert_binding_stack(other)
binding_stack.map { |b| b.eval('self') }.should == other
end
def command_state
evaluate_ruby '_pry_.command_state["cd"]'
end
def old_stack
evaluate_ruby '_pry_.command_state["cd"].old_stack.dup'
end
def evaluate_self
evaluate_ruby 'self'
end
def process_commands(*args)
args.flatten.each do |cmd|
@pry.process_command cmd
end
end
def evaluate_ruby(ruby)
@pry.evaluate_ruby ruby
end
end
describe 'Pry::DefaultCommands::Cd' do
before do
extend CdTestHelpers
@o, @obj = Object.new, Object.new
@obj.instance_variable_set(:@x, 66)
@obj.instance_variable_set(:@y, 79)
@o.instance_variable_set(:@obj, @obj)
# Shortcuts. They save a lot of typing.
@bs1 = "Pad.bs1 = _pry_.binding_stack.dup"
@bs2 = "Pad.bs2 = _pry_.binding_stack.dup"
@bs3 = "Pad.bs3 = _pry_.binding_stack.dup"
@os1 = "Pad.os1 = _pry_.command_state['cd'].old_stack.dup"
@os2 = "Pad.os2 = _pry_.command_state['cd'].old_stack.dup"
@self = "Pad.self = self"
@inner = "Pad.inner = self"
@outer = "Pad.outer = self"
end
after do
Pad.clear
@pry = Pry.new
@pry.binding_stack << Pry.binding_for(@o)
end
describe 'state' do
it 'should not to be set up in fresh instance' do
redirect_pry_io(InputTester.new(@os1, "exit-all")) do
Pry.start(@o)
end
Pad.os1.should == nil
command_state.should.be.nil
end
end
describe 'old stack toggling with `cd -`' do
describe 'in fresh pry instance' do
it 'should not toggle when there is no old stack' do
redirect_pry_io(InputTester.new("cd -", @bs1, "cd -",
@bs2, "exit-all")) do
Pry.start(@o)
2.times do
process_commands 'cd -'
assert_binding_stack [@o]
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o]
Pad.bs2.map { |v| v.eval("self") }.should == [@o]
end
end
describe 'when an error was raised' do
it 'should ensure cd @ raises SyntaxError' do
mock_pry("cd @").should =~ /SyntaxError/
end
it 'should not toggle and should keep correct stacks' do
proc {
process_commands 'cd @'
}.should.raise(Pry::CommandError)
it 'should not toggle and should keep correct old stack' do
redirect_pry_io(InputTester.new("cd @", @os1, "cd -", @os2, "exit-all")) do
Pry.start(@o)
end
old_stack.should == []
assert_binding_stack [@o]
Pad.os1.should == []
Pad.os2.should == []
end
it 'should not toggle and should keep correct current binding stack' do
redirect_pry_io(InputTester.new("cd @", @bs1, "cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o]
Pad.bs2.map { |v| v.eval("self") }.should == [@o]
process_commands 'cd -'
old_stack.should == []
assert_binding_stack [@o]
end
end
describe 'when using simple cd syntax' do
it 'should toggle' do
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd -", @bs1,
"cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd :mon_dogg', 'cd -'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval("self") }.should == [@o]
Pad.bs2.map { |v| v.eval("self") }.should == [@o, :mon_dogg]
process_commands 'cd -'
assert_binding_stack [@o, :mon_dogg]
end
end
describe "when using complex cd syntax" do
it 'should toggle with a complex path (simple case)' do
redirect_pry_io(InputTester.new("cd 1/2/3", "cd -", @bs1,
"cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd 1/2/3', 'cd -'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval('self') }.should == [@o]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, 1, 2, 3]
process_commands 'cd -'
assert_binding_stack [@o, 1, 2, 3]
end
it 'should toggle with a complex path (more complex case)' do
redirect_pry_io(InputTester.new("cd 1/2/3", "cd ../4", "cd -",
@bs1, "cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd 1/2/3', 'cd ../4', 'cd -'
assert_binding_stack [@o, 1, 2, 3]
Pad.bs1.map { |v| v.eval('self') }.should == [@o, 1, 2, 3]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, 1, 2, 4]
process_commands 'cd -'
assert_binding_stack [@o, 1, 2, 4]
end
end
describe 'series of cd calls' do
it 'should toggle with fuzzy `cd -` calls' do
redirect_pry_io(InputTester.new("cd :mon_dogg", "cd -", "cd 42", "cd -",
@bs1, "cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd :mon_dogg', 'cd -', 'cd 42', 'cd -'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval('self') }.should == [@o]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, 42]
process_commands 'cd -'
assert_binding_stack [@o, 42]
end
end
describe 'when using cd ..' do
it 'should toggle with a simple path' do
redirect_pry_io(InputTester.new("cd :john_dogg", "cd ..", @bs1,
"cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd :john_dogg', 'cd ..'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval('self') }.should == [@o]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, :john_dogg]
process_commands 'cd -'
assert_binding_stack [@o, :john_dogg]
end
it 'should toggle with a complex path' do
redirect_pry_io(InputTester.new("cd 1/2/3/../4", "cd -", @bs1,
"cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd 1/2/3/../4', 'cd -'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval('self') }.should == [@o]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, 1, 2, 4]
process_commands 'cd -'
assert_binding_stack [@o, 1, 2, 4]
end
end
describe 'when using cd ::' do
it 'should toggle' do
redirect_pry_io(InputTester.new("cd ::", "cd -", @bs1,
"cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd ::', 'cd -'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval('self') }.should == [@o]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, TOPLEVEL_BINDING.eval("self")]
process_commands 'cd -'
assert_binding_stack [@o, TOPLEVEL_BINDING.eval('self')]
end
end
describe 'when using cd /' do
it 'should toggle' do
redirect_pry_io(InputTester.new("cd /", "cd -", @bs1, "cd :john_dogg",
"cd /", "cd -", @bs2, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd /', 'cd -'
assert_binding_stack [@o]
Pad.bs1.map { |v| v.eval('self') }.should == [@o]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, :john_dogg]
process_commands 'cd :john_dogg', 'cd /', 'cd -'
assert_binding_stack [@o, :john_dogg]
end
end
describe 'when using ^D (Control-D) key press' do
before do
@control_d = "Pry::DEFAULT_CONTROL_D_HANDLER.call('', _pry_)"
end
it 'should keep correct old binding' do
redirect_pry_io(InputTester.new("cd :john_dogg", "cd :mon_dogg",
"cd :kyr_dogg", @control_d, @bs1,
"cd -", @bs2, "cd -", @bs3, "exit-all")) do
Pry.start(@o)
end
process_commands 'cd :john_dogg', 'cd :mon_dogg', 'cd :kyr_dogg'
evaluate_ruby 'Pry::DEFAULT_CONTROL_D_HANDLER.call("", _pry_)'
assert_binding_stack [@o, :john_dogg, :mon_dogg]
Pad.bs1.map { |v| v.eval('self') }.should == [@o, :john_dogg, :mon_dogg]
Pad.bs2.map { |v| v.eval('self') }.should == [@o, :john_dogg, :mon_dogg, :kyr_dogg]
Pad.bs3.map { |v| v.eval('self') }.should == [@o, :john_dogg, :mon_dogg]
process_commands 'cd -'
assert_binding_stack [@o, :john_dogg, :mon_dogg, :kyr_dogg]
process_commands 'cd -'
assert_binding_stack [@o, :john_dogg, :mon_dogg]
end
end
end
it 'should cd into simple input' do
redirect_pry_io(InputTester.new("cd :mon_ouie", @inner, "exit-all")) do
Pry.start(@o)
end
Pad.inner.should == :mon_ouie
process_commands 'cd :mon_ouie'
evaluate_self.should == :mon_ouie
end
it 'should break out of session with cd ..' do
redirect_pry_io(InputTester.new("cd :inner", @inner, "cd ..", @outer, "exit-all")) do
Pry.start(:outer)
end
process_commands 'cd :outer', 'cd :inner'
evaluate_self.should == :inner
Pad.inner.should == :inner
Pad.outer.should == :outer
process_commands 'cd ..'
evaluate_self.should == :outer
end
it "should not leave the REPL session when given 'cd ..'" do
redirect_pry_io(InputTester.new("cd ..", @self, "exit-all")) do
Pry.start(@o)
end
Pad.self.should == @o
process_commands 'cd ..'
evaluate_self.should == @o
end
it 'should break out to outer-most session with cd /' do
redirect_pry_io(InputTester.new("cd :inner", @inner, "cd 5", "Pad.five = self",
"cd /", @outer, "exit-all")) do
Pry.start(:outer)
end
process_commands 'cd :inner'
evaluate_self.should == :inner
Pad.inner.should == :inner
Pad.five.should == 5
Pad.outer.should == :outer
process_commands 'cd 5'
evaluate_self.should == 5
process_commands 'cd /'
evaluate_self.should == @o
end
it 'should break out to outer-most session with just cd (no args)' do
redirect_pry_io(InputTester.new("cd :inner", @inner, "cd 5", "Pad.five = self",
"cd", @outer, "exit-all")) do
Pry.start(:outer)
end
process_commands 'cd :inner'
evaluate_self.should == :inner
Pad.inner.should == :inner
Pad.five.should == 5
Pad.outer.should == :outer
process_commands 'cd 5'
evaluate_self.should == 5
process_commands 'cd'
evaluate_self.should == @o
end
it 'should cd into an object and its ivar using cd obj/@ivar syntax' do
redirect_pry_io(InputTester.new("cd @obj/@x", @bs1, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o, @obj, 66]
process_commands 'cd @obj/@x'
assert_binding_stack [@o, @obj, 66]
end
it 'should cd into an object and its ivar using cd obj/@ivar/ syntax (note following /)' do
redirect_pry_io(InputTester.new("cd @obj/@x/", @bs1, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o, @obj, 66]
process_commands 'cd @obj/@x/'
assert_binding_stack [@o, @obj, 66]
end
it 'should cd into previous object and its local using cd ../local syntax' do
redirect_pry_io(InputTester.new("cd @obj", "local = :local", "cd @x",
"cd ../local", @bs1, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o, @obj, :local]
process_commands 'cd @obj'
evaluate_ruby 'local = :local'
process_commands 'cd @x', 'cd ../local'
assert_binding_stack [@o, @obj, :local]
end
it 'should cd into an object and its ivar and back again using cd obj/@ivar/.. syntax' do
redirect_pry_io(InputTester.new("cd @obj/@x/..", @bs1, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o, @obj]
process_commands 'cd @obj/@x/..'
assert_binding_stack [@o, @obj]
end
it 'should cd into an object and its ivar and back and then into another ivar using cd obj/@ivar/../@y syntax' do
redirect_pry_io(InputTester.new("cd @obj/@x/../@y", @bs1, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o, @obj, 79]
process_commands 'cd @obj/@x/../@y'
assert_binding_stack [@o, @obj, 79]
end
it 'should cd back to top-level and then into another ivar using cd /@ivar/ syntax' do
redirect_pry_io(InputTester.new("@z = 20", "cd @obj/@x/", "cd /@z",
@bs1, "exit-all")) do
Pry.start(@o)
end
Pad.bs1.map { |v| v.eval("self") }.should == [@o, 20]
evaluate_ruby '@z = 20'
process_commands 'cd @obj/@x/', 'cd /@z'
assert_binding_stack [@o, 20]
end
it 'should start a session on TOPLEVEL_BINDING with cd ::' do
redirect_pry_io(InputTester.new("cd ::", @self, "exit-all")) do
Pry.start(@o)
end
Pad.self.should == TOPLEVEL_BINDING.eval("self")
process_commands 'cd ::'
evaluate_self.should == TOPLEVEL_BINDING.eval('self')
end
it 'should cd into complex input (with spaces)' do
@ -292,30 +250,23 @@ describe 'Pry::DefaultCommands::Cd' do
:mon_ouie
end
redirect_pry_io(InputTester.new("cd hello 1, 2, 3", @self, "exit-all")) do
Pry.start(@o)
end
Pad.self.should == :mon_ouie
process_commands 'cd hello 1, 2, 3'
evaluate_self.should == :mon_ouie
end
it 'should not cd into complex input when it encounters an exception' do
redirect_pry_io(InputTester.new("cd 1/2/swoop_a_doop/3",
@bs1, "exit-all")) do
Pry.start(@o)
end
proc {
process_commands 'cd 1/2/swoop_a_doop/3'
}.should.raise(Pry::CommandError)
Pad.bs1.map { |v| v.eval("self") }.should == [@o]
assert_binding_stack [@o]
end
# Regression test for ticket #516.
#it 'should be able to cd into the Object BasicObject.' do
# mock_pry('cd BasicObject.new').should.not =~ /\Aundefined method `__binding__'/
#end
# Regression test for ticket #516
# Possibly move higher up.
it 'should not fail with undefined BasicObject#is_a?' do
mock_pry('cd BasicObject.new').should.not =~ /undefined method `is_a\?'/
end
# FIXME: This is actually broken.
# it 'should be able to cd into the Object BasicObject' do
# proc {
# process_commands 'cd BasicObject.new'
# }.should.not.raise
# end
end

View File

@ -138,17 +138,11 @@ describe "Sticky locals (_file_ and friends)" do
end
it 'should provide different values for successive block invocations' do
o = Object.new
pi = Pry.new
v = [1, 2]
pi.add_sticky_local(:test_local) { v.shift }
pi.input = InputTester.new("@value1 = test_local",
"@value2 = test_local", "exit-all")
pi.output = StringIO.new
pi.repl(o)
o.instance_variable_get(:@value1).should == 1
o.instance_variable_get(:@value2).should == 2
pry = Pry.new
pry.add_sticky_local(:test_local) { rand }
value1 = pry.evaluate_ruby 'test_local'
value2 = pry.evaluate_ruby 'test_local'
value1.should.not == value2
end
end