Revision: 4317 http://trac.macosforge.org/projects/ruby/changeset/4317 Author: eloy.de.enige@gmail.com Date: 2010-07-04 05:32:27 -0700 (Sun, 04 Jul 2010) Log Message: ----------- Update DietRB to 75ec79, which includes the specs: $ rake spec:irb Modified Paths: -------------- MacRuby/trunk/lib/irb/formatter.rb MacRuby/trunk/rakelib/spec.rake MacRuby/trunk/spec/macruby.mspec Added Paths: ----------- MacRuby/trunk/spec/dietrb/ MacRuby/trunk/spec/dietrb/context_spec.rb MacRuby/trunk/spec/dietrb/ext/ MacRuby/trunk/spec/dietrb/ext/colorize_spec.rb MacRuby/trunk/spec/dietrb/ext/completion_spec.rb MacRuby/trunk/spec/dietrb/ext/history_spec.rb MacRuby/trunk/spec/dietrb/formatter_spec.rb MacRuby/trunk/spec/dietrb/regression/ MacRuby/trunk/spec/dietrb/regression/context_spec.rb MacRuby/trunk/spec/dietrb/source_spec.rb MacRuby/trunk/spec/dietrb/spec_helper.rb Modified: MacRuby/trunk/lib/irb/formatter.rb =================================================================== --- MacRuby/trunk/lib/irb/formatter.rb 2010-07-02 13:13:23 UTC (rev 4316) +++ MacRuby/trunk/lib/irb/formatter.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -38,7 +38,9 @@ def inspect_object(object) if @inspect - object.respond_to?(:pretty_inspect) ? object.pretty_inspect : object.inspect + result = object.respond_to?(:pretty_inspect) ? object.pretty_inspect : object.inspect + result.strip! + result else address = object.__id__ * 2 address += 0x100000000 if address < 0 Modified: MacRuby/trunk/rakelib/spec.rake =================================================================== --- MacRuby/trunk/rakelib/spec.rake 2010-07-02 13:13:23 UTC (rev 4316) +++ MacRuby/trunk/rakelib/spec.rake 2010-07-04 12:32:27 UTC (rev 4317) @@ -44,6 +44,11 @@ mspec :ci, ":library" end + desc "Run the DietRB specs" + task :irb do + mspec :ci, "./spec/dietrb" + end + desc "Run language examples that are known to fail" task :fails do mspec :run, "-g fails :full" Added: MacRuby/trunk/spec/dietrb/context_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/context_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/context_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,244 @@ +require File.expand_path('../spec_helper', __FILE__) +require 'tempfile' + +def stub_Readline + class << Readline + attr_reader :received + + def stub_input(*input) + @input = input + end + + def readline(prompt, history) + @received = [prompt, history] + @input.shift + end + end +end +stub_Readline + +class TestProcessor + def input(s) + s * 2 + end +end + +main = self + +describe "IRB::Context" do + before do + @context = IRB::Context.new(main) + end + + it "initializes with an object and stores a copy of its binding" do + @context.object.should == main + eval("self", @context.binding).should == main + eval("x = :ok", @context.binding) + eval("y = x", @context.binding) + eval("y", @context.binding).should == :ok + end + + it "initializes with an object and an explicit binding" do + context = IRB::Context.new(Object.new, TOPLEVEL_BINDING) + eval("class InTopLevel; end", context.binding) + lambda { ::InTopLevel }.should_not raise_error(NameError) + end + + it "initializes with an 'empty' state" do + @context.line.should == 1 + @context.source.class.should == IRB::Source + @context.source.to_s.should == "" + end + + it "initializes with an instance of each processor" do + before = IRB::Context.processors.dup + begin + IRB::Context.processors << TestProcessor + @context = IRB::Context.new(main) + @context.processors.last.class.should == TestProcessor + ensure + IRB::Context.processors.replace(before) + end + end + + it "does not use the same binding copy of the top level object" do + lambda { eval("x", @context.binding) }.should raise_error(NameError) + end + + it "makes itself the current running context during the runloop and resigns once it's done" do + IRB::Context.current.should == nil + + Readline.stub_input("current_during_run = IRB::Context.current") + @context.run + eval('current_during_run', @context.binding).should == @context + + IRB::Context.current.should == nil + end +end + +describe "IRB::Context, when evaluating source" do + before do + @context = IRB::Context.new(main) + def @context.printed; @printed ||= '' end + def @context.puts(string); printed << "#{string}\n" end + IRB.formatter = IRB::Formatter.new + end + + it "evaluates code with the object's binding" do + @context.__evaluate__("self").should == main + end + + it "prints the result" do + @context.evaluate("Hash[:foo, :foo]") + @context.printed.should == "=> {:foo=>:foo}\n" + end + + it "assigns the result to the local variable `_'" do + result = @context.evaluate("Object.new") + @context.evaluate("_").should == result + @context.evaluate("_").should == result + end + + it "coerces the given source to a string first" do + o = Object.new + def o.to_s; "self"; end + @context.evaluate(o).should == main + end + + it "rescues any type of exception" do + lambda { + @context.evaluate("DoesNotExist") + @context.evaluate("raise Exception") + }.should_not.raise_error + end + + it "assigns the last raised exception to the global variable `$EXCEPTION' / `$e'" do + @context.evaluate("DoesNotExist") + $EXCEPTION.class.should == NameError + $EXCEPTION.message.should include('DoesNotExist') + $e.should == $EXCEPTION + end + + it "prints the exception that occurs" do + @context.evaluate("DoesNotExist") + @context.printed.should =~ /^NameError:.+DoesNotExist/ + end + + it "uses the line number of the *first* line in the buffer, for the line parameter of eval" do + @context.process_line("DoesNotExist") + @context.printed.should =~ /\(irb\):1:in/ + @context.process_line("class A") + @context.process_line("DoesNotExist") + @context.process_line("end") + @context.printed.should =~ /\(irb\):3:in.+\(irb\):2:in/m + end + + it "inputs a line to be processed, skipping readline" do + expected = "#{@context.formatter.prompt(@context)}2 * 21\n=> 42\n" + @context.input_line("2 * 21") + @context.printed.should == expected + end +end + +describe "IRB::Context, when receiving input" do + before do + @context = IRB::Context.new(main) + end + + it "prints the prompt, reads a line, saves it to the history and returns it" do + Readline.stub_input("def foo") + @context.readline.should == "def foo" + Readline.received.should == ["irb(main):001:0> ", true] + end + + it "passes the input to all processors, which may return a new value" do + @context.processors << TestProcessor.new + Readline.stub_input("foo") + @context.readline.should == "foofoo" + end + + it "processes the output" do + Readline.stub_input("def foo") + def @context.process_line(line); @received = line; false; end + @context.run + @context.instance_variable_get(:@received).should == "def foo" + end + + it "adds the received code to the source buffer" do + @context.process_line("def foo") + @context.process_line("p :ok") + @context.source.to_s.should == "def foo\np :ok" + end + + it "clears the source buffer when an Interrupt signal is received" do + begin + @context.process_line("def foo") + + def Readline.readline(*args) + unless @raised + @raised = true + raise Interrupt + end + end + + lambda { @context.run }.should_not raise_error(Interrupt) + @context.source.to_s.should == "" + ensure + stub_Readline + end + end + + it "increases the current line number" do + @context.line.should == 1 + @context.process_line("def foo") + @context.line.should == 2 + @context.process_line("p :ok") + @context.line.should == 3 + end + + it "evaluates the buffered source once it's a valid code block" do + def @context.evaluate(source); @evaled = source; end + + @context.process_line("def foo") + @context.process_line(":ok") + @context.process_line("end; p foo") + + source = @context.instance_variable_get(:@evaled) + source.to_s.should == "def foo\n:ok\nend; p foo" + end + + it "prints that a syntax error occurred on the last line and reset the buffer to the previous line" do + def @context.puts(str); @printed = str; end + + @context.process_line("def foo") + @context.process_line(" };") + + @context.source.to_s.should == "def foo" + printed = @context.instance_variable_get(:@printed) + printed.should == "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'" + end + + it "returns whether or not the runloop should continue, but only if the level is 0" do + @context.process_line("def foo").should == true + @context.process_line("quit").should == true + @context.process_line("end").should == true + + @context.process_line("quit").should == false + end + + it "exits the runloop if the user wishes so" do + Readline.stub_input("quit", "def foo") + def @context.process_line(line); @received = line; super; end + @context.run + @context.instance_variable_get(:@received).should_not == "def foo" + end +end + +describe "Kernel::irb" do + it "creates a new context for the given object and runs it" do + Readline.stub_input("::IRBRan = self") + o = Object.new + irb(o) + IRBRan.should == o + end +end Added: MacRuby/trunk/spec/dietrb/ext/colorize_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/ext/colorize_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/ext/colorize_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,58 @@ +require File.expand_path('../../spec_helper', __FILE__) +require 'irb/ext/colorize' + +main = self + +describe "IRB::ColoredFormatter" do + before do + @formatter = IRB::ColoredFormatter.new + @context = IRB::Context.new(main) + end + + it "colorizes a constant" do + @formatter.result(Hash).should == "=> \e[1;32mHash\e[0;0m" + end + + it "colorizes a numeric" do + @formatter.result(1).should == "=> \e[0;36m1\e[0;0m" + @formatter.result(1.2).should == "=> \e[0;36m1.2\e[0;0m" + end + + # Not Wirble compliant + it "colorizes a Range" do + # @formatter.result(1..3).should == "=> \e[0;36m1\e[0;0m\e[0;31m..\e[0;0m\e[0;36m3\e[0;0m" + @formatter.result(1..3).should == "=> \e[0;36m1\e[0;0m\e[0;34m..\e[0;0m\e[0;36m3\e[0;0m" + end + + it "colorizes a String" do + @formatter.result("foo bar").should == "=> \e[0;31m\"\e[0;0m\e[0;36mfoo bar\e[0;0m\e[0;31m\"\e[0;0m" + end + + it "colorizes a Hash" do + @formatter.result(:ok => :foo).should == "=> \e[0;32m{\e[0;0m\e[1;33m:\e[0;0m\e[1;33mok\e[0;0m\e[0;34m=>\e[0;0m\e[1;33m:\e[0;0m\e[1;33mfoo\e[0;0m\e[0;32m}\e[0;0m" + end + + it "colorizes an Array" do + @formatter.result([1, 2]).should == "=> \e[0;32m[\e[0;0m\e[0;36m1\e[0;0m\e[0;34m,\e[0;0m \e[0;36m2\e[0;0m\e[0;32m]\e[0;0m" + end + + it "colorizes the prompt" do + @formatter.colors[:prompt] = :light_green + @formatter.prompt(@context).should == "\e[1;32m#{IRB::Formatter.new.prompt(@context)}\e[0;0m" + end + + it "colorizes the result prefix" do + @formatter.colors[:result_prefix] = :light_red + @formatter.result("").should == "\e[1;31m=>\e[0;0m \e[0;31m\"\e[0;0m\e[0;31m\"\e[0;0m" + end + + it "uses the dark_background color scheme by default" do + @formatter.color_scheme.should == :dark_background + end + + it "changes color scheme" do + @formatter.color_scheme = :fresh + @formatter.color_scheme.should == :fresh + @formatter.colors[:result_prefix].should == :light_purple + end +end \ No newline at end of file Added: MacRuby/trunk/spec/dietrb/ext/completion_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/ext/completion_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/ext/completion_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,237 @@ +require File.expand_path('../../spec_helper', __FILE__) +require 'irb/ext/completion' + +module CompletionHelper + def complete(str) + IRB::Completion.new(@context, str).results + end + + def imethods(klass, receiver = nil) + klass.instance_methods.map { |m| [receiver, m.to_s].compact.join('.') }.sort + end + + def methods(object, receiver = nil) + object.methods.map { |m| [receiver, m.to_s].compact.join('.') }.sort + end +end + +class CompletionStub + def self.a_cmethod + end + + def an_imethod + end +end + +class Playground + CompletionStub = Object.new + def CompletionStub.a_singleton_method; end + + def a_local_method; end +end + +$a_completion_stub = CompletionStub.new + +describe "IRB::Completion" do + extend CompletionHelper + + before do + @context = IRB::Context.new(Playground.new) + end + + it "quacks like a Proc" do + IRB::Completion.call('//.').should == imethods(Regexp, '//') + end + + describe "when doing a method call on an explicit receiver," do + describe "and the source ends with a period," do + describe "returns *all* public methods of the receiver that" do + it "matches as a local variable" do + @context.__evaluate__('foo = ::CompletionStub.new') + complete('foo.').should == imethods(::CompletionStub, 'foo') + + @context.__evaluate__('def foo.singleton_method; end') + complete('foo.').should include('foo.singleton_method') + end + + it "matches as a global variable" do + complete('$a_completion_stub.').should == imethods(::CompletionStub, '$a_completion_stub') + end + + # TODO: fix + # it "matches as a local constant" do + # complete('CompletionStub.').should == methods(Playground::CompletionStub) + # end + + it "matches as a top level constant" do + complete('::CompletionStub.').should == methods(::CompletionStub, '::CompletionStub') + end + end + + describe "returns *all* public instance methods of the class (the receiver) that" do + it "matches as a Regexp literal" do + complete('//.').should == imethods(Regexp, '//') + complete('/^(:[^:.]+)\.([^.]*)$/.').should == imethods(Regexp, '/^(:[^:.]+)\.([^.]*)$/') + complete('/^(#{oops})\.([^.]*)$/.').should == imethods(Regexp, '/^(#{oops})\.([^.]*)$/') + complete('%r{/foo/.*/bar}.').should == imethods(Regexp, '%r{/foo/.*/bar}') + end + + it "matches as an Array literal" do + complete('[].').should == imethods(Array, '[]') + complete('[:ok, {}, "foo",].').should == imethods(Array, '[:ok, {}, "foo",]') + complete('[*foo].').should == imethods(Array, '[*foo]') + complete('%w{foo}.').should == imethods(Array, '%w{foo}') + complete('%W{#{:foo}}.').should == imethods(Array, '%W{#{:foo}}') + end + + # fails on MacRuby + it "matches as a lambda literal" do + complete('->{}.').should == imethods(Proc, '->{}') + complete('->{x=:ok}.').should == imethods(Proc, '->{x=:ok}') + complete('->(x){x=:ok}.').should == imethods(Proc, '->(x){x=:ok}') + end + + it "matches as a Hash literal" do + complete('{}.').should == imethods(Hash, '{}') + complete('{:foo=>:bar,}.').should == imethods(Hash, '{:foo=>:bar,}') + complete('{foo:"bar"}.').should == imethods(Hash, '{foo:"bar"}') + end + + it "matches as a Symbol literal" do + complete(':foo.').should == imethods(Symbol, ':foo') + complete(':"foo.bar".').should == imethods(Symbol, ':"foo.bar"') + complete(':"foo.#{"bar"}".').should == imethods(Symbol, ':"foo.#{"bar"}"') + complete(':\'foo.#{"bar"}\'.').should == imethods(Symbol, ':\'foo.#{"bar"}\'') + complete('%s{foo.bar}.').should == imethods(Symbol, '%s{foo.bar}') + end + + it "matches as a String literal" do + complete("'foo\\'bar'.").should == imethods(String, "'foo\\'bar'") + complete('"foo\"bar".').should == imethods(String, '"foo\"bar"') + complete('"foo#{"bar"}".').should == imethods(String, '"foo#{"bar"}"') + complete('%{foobar}.').should == imethods(String, '%{foobar}') + complete('%q{foo#{:bar}}.').should == imethods(String, '%q{foo#{:bar}}') + complete('%Q{foo#{:bar}}.').should == imethods(String, '%Q{foo#{:bar}}') + end + + it "matches as a Range literal" do + complete('1..10.').should == imethods(Range, '1..10') + complete('1...10.').should == imethods(Range, '1...10') + complete('"a".."z".').should == imethods(Range, '"a".."z"') + complete('"a"..."z".').should == imethods(Range, '"a"..."z"') + end + + it "matches as a Fixnum literal" do + complete('42.').should == imethods(Fixnum, '42') + complete('+42.').should == imethods(Fixnum, '+42') + complete('-42.').should == imethods(Fixnum, '-42') + complete('42_000.').should == imethods(Fixnum, '42_000') + end + + it "matches as a Bignum literal as a Fixnum" do + complete('100_000_000_000_000_000_000.').should == imethods(Fixnum, '100_000_000_000_000_000_000') + complete('-100_000_000_000_000_000_000.').should == imethods(Fixnum, '-100_000_000_000_000_000_000') + complete('+100_000_000_000_000_000_000.').should == imethods(Fixnum, '+100_000_000_000_000_000_000') + end + + it "matches as a Float with exponential literal" do + complete('1.2e-3.').should == imethods(Float, '1.2e-3') + complete('+1.2e-3.').should == imethods(Float, '+1.2e-3') + complete('-1.2e-3.').should == imethods(Float, '-1.2e-3') + end + + it "matches as a hex literal as a Fixnum" do + complete('0xffff.').should == imethods(Fixnum, '0xffff') + complete('+0xffff.').should == imethods(Fixnum, '+0xffff') + complete('-0xffff.').should == imethods(Fixnum, '-0xffff') + end + + it "matches as a binary literal as a Fixnum" do + complete('0b01011.').should == imethods(Fixnum, '0b01011') + complete('-0b01011.').should == imethods(Fixnum, '-0b01011') + complete('+0b01011.').should == imethods(Fixnum, '+0b01011') + end + + it "matches as an octal literal as a Fixnum" do + complete('0377.').should == imethods(Fixnum, '0377') + complete('-0377.').should == imethods(Fixnum, '-0377') + complete('+0377.').should == imethods(Fixnum, '+0377') + end + + it "matches as a Float literal" do + complete('42.0.').should == imethods(Float, '42.0') + complete('-42.0.').should == imethods(Float, '-42.0') + complete('+42.0.').should == imethods(Float, '+42.0') + complete('42_000.0.').should == imethods(Float, '42_000.0') + end + + it "matches as a Bignum float literal as a Float" do + complete('100_000_000_000_000_000_000.0.').should == imethods(Float, '100_000_000_000_000_000_000.0') + complete('+100_000_000_000_000_000_000.0.').should == imethods(Float, '+100_000_000_000_000_000_000.0') + complete('-100_000_000_000_000_000_000.0.').should == imethods(Float, '-100_000_000_000_000_000_000.0') + end + end + + it "returns *all* public instance methods of the class (the receiver) that ::new is called on" do + complete("Playground.new.").should == imethods(Playground, 'Playground.new') + complete("Playground.new.a_local_m").should == %w{ Playground.new.a_local_method } + + @context.__evaluate__("klass = Playground") + complete("klass.new.").should == imethods(Playground, 'klass.new') + complete("klass.new.a_local_m").should == %w{ klass.new.a_local_method } + end + end + + describe "and the source does *not* end with a period," do + it "filters the methods, of the literal receiver, by the given method name" do + complete('//.nam').should == %w{ //.named_captures //.names } + complete('//.named').should == %w{ //.named_captures } + end + + it "filters the methods, of the variable receiver, by the given method name" do + @context.__evaluate__('foo = ::CompletionStub.new') + complete('foo.an_im').should == %w{ foo.an_imethod } + complete('$a_completion_stub.an_im').should == %w{ $a_completion_stub.an_imethod } + # TODO: fix + # complete('CompletionStub.a_sing').should == %w{ CompletionStub.a_singleton_method } + end + end + end + + describe "when *not* doing a method call on an explicit receiver" do + before do + @context.__evaluate__("a_local_variable = :ok") + end + + it "matches local variables" do + complete("a_local_v").should == %w{ a_local_variable } + end + + it "matches instance methods of the context object" do + complete("a_local_m").should == %w{ a_local_method } + end + + it "matches local variables and instance method of the context object" do + complete("a_loc").should == %w{ a_local_method a_local_variable } + end + + it "matches global variables" do + complete("$a_completion_s").should == %w{ $a_completion_stub } + end + + it "matches constants" do + complete("Playgr").should == %w{ Playground } + end + + it "matches top level constants" do + complete("::CompletionSt").should == %w{ ::CompletionStub } + end + end + + it "completes reserved words as variables or constants" do + (IRB::Completion::RESERVED_DOWNCASE_WORDS + + IRB::Completion::RESERVED_UPCASE_WORDS).each do |word| + complete(word[0..-2]).should include(word) + end + end +end \ No newline at end of file Added: MacRuby/trunk/spec/dietrb/ext/history_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/ext/history_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/ext/history_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,187 @@ +require File.expand_path('../../spec_helper', __FILE__) +require "tempfile" + +describe "IRB::History, by default," do + it "stores the history in ~/.irb_history" do + IRB::History.file.should == File.expand_path("~/.irb_history") + end +end + +describe "IRB::History" do + before do + @file = Tempfile.new("irb_history.txt") + IRB::History.file = @file.path + @history = IRB::History.new(nil) + end + + after do + @file.close + end + + it "adds input to the history file" do + @history.input "puts :ok" + @file.rewind; @file.read.should == "puts :ok\n" + @history.input "foo(x)" + @file.rewind; @file.read.should == "puts :ok\nfoo(x)\n" + end + + it "returns the same input value" do + @history.input("foo(x)").should == "foo(x)" + end + + it "returns the contents of the history file as an array of lines" do + @history.input "puts :ok" + @history.to_a.should == ["puts :ok"] + @history.input "foo(x)" + @history.to_a.should == ["puts :ok", "foo(x)"] + end + + it "returns an empty array if the history file doesn't exist yet and create it once input is added" do + @file.close + FileUtils.rm(@file.path) + + @history.to_a.should == [] + File.exist?(@file.path).should == false + + @history.input "puts :ok" + File.exist?(@file.path).should == true + @history.to_a.should == ["puts :ok"] + end + + it "stores the contents of the history file in Readline::HISTORY once" do + Readline::HISTORY.clear + + @history.input "puts :ok" + @history.input "foo(x)" + + IRB::History.new(nil) + IRB::History.new(nil) + + Readline::HISTORY.to_a.should == ["puts :ok", "foo(x)"] + end + + it "clears the history and history file" do + @history.input "puts :ok" + @history.input "foo(x)" + @history.clear! + + @file.rewind; @file.read.should == "" + Readline::HISTORY.to_a.should == [] + end +end + +class IRB::History + def printed + @printed ||= "" + end + + def print(s) + printed << s + end + + def puts(s) + printed << "#{s}\n" + end + + def clear! + @cleared = true + end + def cleared? + @cleared + end +end + +describe "IRB::History, concerning the user api, by default," do + it "shows a maximum of 50 history entries" do + IRB::History.max_entries_in_overview.should == 50 + end +end + +describe "IRB::History, concerning the user api," do + before do + sources = [ + "puts :ok", + "x = foo(x)", + "class AAA", + " def bar", + " :ok", + " end", + "end", + "THIS LINE REPRESENTS THE ENTERED COMMAND AND SHOULD BE OMITTED!" + ] + + Readline::HISTORY.clear + sources.each { |source| Readline::HISTORY.push(source) } + + IRB::History.max_entries_in_overview = 5 + + @context = IRB::Context.new(Object.new) + IRB::Context.current = @context + + @history = @context.processors.find { |p| p.is_a?(IRB::History) } + end + + after do + IRB::Context.current = nil + end + + it "returns nil so that IRB doesn't cache some arbitrary line number" do + history.should == nil + end + + it "prints a formatted list with, by default IRB::History.max_entries_in_overview, number of history entries" do + history + + @history.printed.should == %{ +2: class AAA +3: def bar +4: :ok +5: end +6: end +}.sub(/\n/, '') + end + + it "prints a formatted list of N most recent history entries" do + history(7) + + @history.printed.should == %{ +0: puts :ok +1: x = foo(x) +2: class AAA +3: def bar +4: :ok +5: end +6: end +}.sub(/\n/, '') + end + + it "prints a formatted list of all history entries if the request number of entries is more than there is" do + history(777) + + @history.printed.should == %{ +0: puts :ok +1: x = foo(x) +2: class AAA +3: def bar +4: :ok +5: end +6: end +}.sub(/\n/, '') + end + + it "evaluates the history entry specified" do + @context.__evaluate__("x = 2; def foo(x); x * 2; end") + history! 1 + @context.__evaluate__("x").should == 4 + end + + it "evaluates the history entries specified by a range" do + history! 2..6 + @context.__evaluate__("AAA.new.bar").should == :ok + end + + it "clears the history and history file" do + clear_history! + @history.cleared?.should == true + end +end \ No newline at end of file Added: MacRuby/trunk/spec/dietrb/formatter_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/formatter_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/formatter_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,78 @@ +require File.expand_path('../spec_helper', __FILE__) + +main = self + +describe "IRB::Formatter" do + before do + @formatter = IRB::Formatter.new + @context = IRB::Context.new(main) + end + + it "returns a prompt string, displaying line number and code indentation level" do + @formatter.prompt(@context).should == "irb(main):001:0> " + @context.instance_variable_set(:@line, 23) + @formatter.prompt(@context).should == "irb(main):023:0> " + @context.source << "def foo" + @formatter.prompt(@context).should == "irb(main):023:1> " + end + + it "describes the context's object in the prompt" do + o = Object.new + @formatter.prompt(IRB::Context.new(o)).should == "irb(#{o.inspect}):001:0> " + end + + it "returns a very simple prompt if specified" do + @formatter.prompt = :simple + @formatter.prompt(@context).should == ">> " + end + + it "returns no prompt if specified" do + @formatter.prompt = nil + @formatter.prompt(@context).should == "" + end + + it "returns a formatted exception message, with the lines, regarding dietrb, filtered out of the backtrace" do + begin; @context.__evaluate__('DoesNotExist'); rescue NameError => e; exception = e; end + backtrace = exception.backtrace.reject { |f| f =~ /#{ROOT}/ } + @formatter.exception(exception).should == + "NameError: uninitialized constant IRB::Context::DoesNotExist\n\t#{backtrace.join("\n\t")}" + end + + it "does not filter the backtrace if $DEBUG is true" do + begin + before, $DEBUG = $DEBUG, true + + begin; @context.__evaluate__('DoesNotExist'); rescue NameError => e; exception = e; end + @formatter.exception(exception).should == + "NameError: uninitialized constant IRB::Context::DoesNotExist\n\t#{exception.backtrace.join("\n\t")}" + ensure + $DEBUG = before + end + end + + it "prints the result" do + @formatter.result(:foo => :foo).should == "=> {:foo=>:foo}" + end + + it "prints the result with object#pretty_inspect, if it responds to it" do + object = Object.new + def object.pretty_inspect; "foo"; end + @formatter.result(object).should == "=> foo" + end + + it "prints only the class name and memory address in `no inspect' mode" do + @formatter.inspect = false + + object = Object.new + def object.inspect; @inspected = true; "Never called!"; end + def object.__id__; 2158110700; end + + @formatter.result(object).should == "=> #<Object:0x101444fd8>" + object.instance_variable_get(:@inspected).should_not == true + end + + it "prints that a syntax error occurred on the last line and reset the buffer to the previous line" do + @formatter.syntax_error(2, "syntax error, unexpected '}'").should == + "SyntaxError: compile error\n(irb):2: syntax error, unexpected '}'" + end +end \ No newline at end of file Added: MacRuby/trunk/spec/dietrb/regression/context_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/regression/context_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/regression/context_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,16 @@ +require File.expand_path('../../spec_helper', __FILE__) + +main = self + +describe "IRB::Context, when evaluating source" do + before do + @context = IRB::Context.new(main) + def @context.printed; @printed ||= '' end + def @context.puts(string); printed << "#{string}\n" end + end + + it "does not assign the result to the `_' variable in one go, so it doesn't show up in a syntax error" do + @context.evaluate("'banana;") + @context.printed.should_not include("_ = ('banana;)") + end +end \ No newline at end of file Added: MacRuby/trunk/spec/dietrb/source_spec.rb =================================================================== --- MacRuby/trunk/spec/dietrb/source_spec.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/source_spec.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,196 @@ +require File.expand_path('../spec_helper', __FILE__) + +describe "IRB::Source" do + before do + @source = IRB::Source.new + end + + it "initializes with an empty buffer" do + @source.buffer.should == [] + end + + it "appends source to the buffer, removing trailing newlines" do + @source << "foo\n" + @source << "bar\r\n" + @source.buffer.should == %w{ foo bar } + end + + it "ignores empty strings" do + @source << "" + @source << " \n" + @source.buffer.should == [] + end + + it "removes the last line from the buffer" do + @source << "foo\n" + @source << "bar\r\n" + @source.pop.should == "bar" + @source.buffer.should == %w{ foo } + end + + it "returns the full buffered source, joined by newlines" do + @source.source.should == "" + @source << "foo\n" + @source.source.should == "foo" + @source << "bar\r\n" + @source.source.should == "foo\nbar" + end + + it "aliases #to_s to #source" do + @source << "foo" + @source << "bar" + @source.to_s.should == "foo\nbar" + end + + it "returns that the accumulated source is a valid code block" do + [ + ["def foo", "p :ok", "end"], + ["class A; def", "foo(x); p x", "end; end"] + ].each do |buffer| + IRB::Source.new(buffer).code_block?.should == true + end + end + + it "returns that the accumulated source is not a valid code block" do + [ + ["def foo", "p :ok"], + ["class A; def", "foo(x); p x", "end"] + ].each do |buffer| + IRB::Source.new(buffer).code_block?.should == false + end + end + + it "returns whether or not the accumulated source contains a syntax error" do + @source.syntax_error?.should == false + @source << "def foo" + @source.syntax_error?.should == false + @source << " def;" + @source.syntax_error?.should == true + end + + it "returns the current code block indentation level" do + @source.level.should == 0 + @source << "class A" + @source.level.should == 1 + @source << " def foo" + @source.level.should == 2 + @source << " p :ok" + @source.level.should == 2 + @source << " end" + @source.level.should == 1 + @source << " class B" + @source.level.should == 2 + @source << " def bar" + @source.level.should == 3 + @source << " p :ok; end" + @source.level.should == 2 + @source << " end; end" + @source.level.should == 0 + end + + it "caches the reflection when possible" do + @source << "def foo" + reflection = @source.reflect + @source.level + @source.code_block? + @source.reflect.should == reflection + + @source << "end" + @source.level + new_reflection = @source.reflect + new_reflection.should_not == reflection + @source.code_block? + @source.reflect.should == new_reflection + + reflection = new_reflection + + @source.pop + @source.level + new_reflection = @source.reflect + new_reflection.should_not == reflection + @source.syntax_error? + @source.reflect.should == new_reflection + end +end + +describe "IRB::Source::Reflector" do + def reflect(source) + IRB::Source::Reflector.new(source) + end + + it "returns whether or not the source is a valid code block" do + reflect("def foo").code_block?.should == false + reflect("def foo; p :ok").code_block?.should == false + reflect("def foo; p :ok; end").code_block?.should == true + + reflect("if true").code_block?.should == false + reflect("p :ok if true").code_block?.should == true + end + + it "returns whether or not the current session should be terminated" do + reflect("exit").terminate?.should == true + reflect("quit").terminate?.should == true + reflect("def foo; end; exit").terminate?.should == true + reflect("def foo; end; quit").terminate?.should == true + + reflect("def foo; exit; end").terminate?.should == false + reflect("def foo; quit; end").terminate?.should == false + end + + it "returns whether or not the source contains a syntax error, except a code block not ending" do + reflect("def;").syntax_error?.should == true + reflect("def;").syntax_error?.should == true + reflect("def foo").syntax_error?.should == false + reflect("class A; }").syntax_error?.should == true + reflect("class A; {" ).syntax_error?.should == false + reflect("class A def foo").syntax_error?.should == true + reflect("class A; def foo" ).syntax_error?.should == false + end + + it "returns the actual syntax error message if one occurs" do + reflect("def foo").syntax_error.should == nil + reflect("}").syntax_error.should == "syntax error, unexpected '}'" + end + + it "returns the code block indentation level" do + reflect("").level.should == 0 + reflect("class A").level.should == 1 + reflect("class A; def foo").level.should == 2 + reflect("class A; def foo; p :ok").level.should == 2 + reflect("class A; def foo; p :ok; end").level.should == 1 + reflect("class A; class B").level.should == 2 + reflect("class A; class B; def bar").level.should == 3 + reflect("class A; class B; def bar; p :ok; end").level.should == 2 + reflect("class A; class B; def bar; p :ok; end; end; end").level.should == 0 + end + + it "correctly increases and decreases the code block indentation level for keywords" do + [ + "class A", + "module A", + "def foo", + "begin", + "if x == :ok", + "unless x == :ko", + "case x", + "while x", + "for x in xs", + "x.each do" + ].each do |open| + reflect(open).level.should == 1 + reflect("#{open}\nend").level.should == 0 + end + end + + it "correctly increases and decreases the code block indentation level for literals" do + [ + ["lambda { |x|", "}"], + ["{", "}"], + ['"#{', '}"'], + ["[", "]"] + ].each do |open, close| + reflect(open).level.should == 1 + reflect("#{open}\n#{close}").level.should == 0 + end + end +end Added: MacRuby/trunk/spec/dietrb/spec_helper.rb =================================================================== --- MacRuby/trunk/spec/dietrb/spec_helper.rb (rev 0) +++ MacRuby/trunk/spec/dietrb/spec_helper.rb 2010-07-04 12:32:27 UTC (rev 4317) @@ -0,0 +1,17 @@ +unless defined?(MSpec) + require 'rubygems' + require 'mspec' +end + +ENV['SPECCING'] = 'true' + +root = File.expand_path('../../', __FILE__) +if File.basename(root) == 'spec' + # running from the MacRuby repo + ROOT = File.expand_path('../../../', __FILE__) +else + ROOT = root +end +$:.unshift File.join(ROOT, 'lib') + +require 'irb' \ No newline at end of file Modified: MacRuby/trunk/spec/macruby.mspec =================================================================== --- MacRuby/trunk/spec/macruby.mspec 2010-07-02 13:13:23 UTC (rev 4316) +++ MacRuby/trunk/spec/macruby.mspec 2010-07-04 12:32:27 UTC (rev 4317) @@ -68,9 +68,11 @@ ENV['DYLD_LIBRARY_PATH'] = source_root # Setup the proper load paths for lib and extensions load_paths = %w{ -I. -I./lib -I./ext } + load_paths << '-I./ext/ripper/lib' # ripper specific load path fix load_paths.concat Dir.glob('./ext/**/*.bundle').map { |filename| "-I#{File.dirname(filename)}" }.uniq load_paths.concat(get(:flags)) if get(:flags) set :flags, load_paths + # The default implementation to run the specs. set :target, File.join(source_root, 'macruby') end