$: << File.join(File.dirname(__FILE__), 'Test')
require 'Test'

class StackFramesTest < Test

  def initialize(argv)
    @calibrate = false
    argv.each do |arg|
      case arg
      when '--calibrate'
        @calibrate = true
      else
        raise "Invalid argument: #{arg}"
      end
    end
  end

  FrameCheckerFilename = 'StackFramesAssert.rb'

  def test
    levels = (0..8)
    if @calibrate
      puts "Calibrating..."
      levels.each do
        makeStackFrameChecker(stack_frames(0))
      end
    end
    require FrameCheckerFilename
    levels.each do |level|
      checkFrames(stack_frames, level) if level == 1
      checkFrames(stack_frames(level), level)
    end
  end

  def testNegativeLevel
    assertException(ArgumentError, "negative level(-1)") do
      stack_frames(-1)
    end
  end

  def testLevelTooLarge
    assertEquals(nil, stack_frames(100))
  end

  def testInspect
    assertMatches(expectedInspect, stack_frames[-1].inspect)
  end

  def testToS
    assertMatches(expectedInspect, stack_frames[-1].to_s)
  end

  private

  def makeStackFrameChecker(frames)
    File.open(FrameCheckerFilename, "w") do |file|
      file.puts "def checkFrames(frames, level)"
      frames.each_index do |i|
        frame = frames[i]
        file.puts
        file.puts "  if level <= #{i}"
        file.puts "    frame = frames.shift"
        file.puts %Q{    assertEquals("#{frame.class}", frame.class)}
        file.puts %Q{    assertEquals("#{frame.function}", frame.function)}
        if frame.object == self
          file.puts %Q{    assertEquals(self, frame.object)}
        else
          file.puts %Q{    assertNotEquals(self, frame.object)}
        end
        file.puts %Q{    assertEquals("#{frame.file}", frame.file)}
        if i == frames.length - 1
          file.puts %Q{    assertEquals($topLine, frame.line)}
        end
        file.puts "  end"
      end
      file.puts
      file.puts "  assertEquals(frames.length, 0)"
      file.puts
      file.puts "end"
    end
  end

  def expectedInspect
    Regexp.new("#<StackFrame class=Test function=run " \
	       "object=#<StackFramesTest:0x[0-9a-f]+> " \
	       "file=#{$0} line=\\d+>")
  end

end

$topLine = __LINE__ ; StackFramesTest.new(ARGV).run
puts "OK"

