# A simplified test suite that contains only one assert function
# Meant for Ruby beginners so they don't have to figure out how to use one of Ruby's several complicated test suites right away.
class SimpleTestSuite
  LINESPEC_REGEX = /^(.+):(\d+):in `(.+)'$/ # grabs source file, line number, and function from the output of the caller method
  
  def initialize
    @assertions = @passed = @failed = 0
  end

  # Prints an error message if there was an error and updates the counts.
  #
  # got should be the result to be tested. expected should be a known value to test against.
  #
  # assert 2+2, 4 would pass.
  #
  # assert 2+2, 5 would fail and print:
  #
  # assert 2+2, 5 failed. Got 4.
  def assert(got, expected)
    @assertions += 1 # There is no ++ method, because that doesn't make sense when numbers are objects.
    if got == expected
      @passed += 1
      return
    end
    if ((got.kind_of? Float or expected.kind_of? Float) and (got-expected).abs < 0.00001)
      @passed += 1
      return
    end
    @failed += 1
    puts "#{get_line(find_linespec).strip} failed. Got #{got.inspect}."
  end
  
  # Print counts of all assertions: how many there were, how many passed, and how many failed.
  # Then, reset the counts
  def count_assertions
    puts "Testing complete. #{@assertions} assertions. #{@passed} passed. #{@failed} failed."
    @assertions = @passed = @failed = 0
  end
  
private # Everything after this is private within the class
  
  # Go through caller and pick the first line from it that's not in this file
  def find_linespec
    caller.each do |line|
      return line unless line.include? __FILE__
    end
  end
  
  # Pass a string like "/usr/lib/ruby/1.9.1/irb/workspace.rb:80:in `eval'"
  # Returns the line of source code it came from
  def get_line(linespec, lines_nearby=0)
    m = LINESPEC_REGEX.match(linespec)
    if m.nil?
      puts "Couldn't understand linespec: #{linespec}"
      return linespec
    end
    
    sourcefile, lineno = m[1], m[2].to_i # Yes, you can do this.
    
    begin
      # http://stackoverflow.com/questions/4014352/ruby-getting-a-particular-line-from-a-file
      # In Ruby, there's no shame in asking the Internet. Chances are the API has the function
      # you want - you just don't know it yet.
      File.open(sourcefile,'r') do |f| # This version of open autocloses the file when it exits the block
        f.gets until f.lineno == lineno - 1
        return f.gets
      end
    rescue Exception => e
      puts "Error loading source file #{sourcefile}"
      return linespec
    end
  end
end

DEFAULT_TESTER = SimpleTestSuite.new

# Prints an error message if there was an error and updates the counts.
#
# got should be the result to be tested. expected should be a known value to test against.
#
# assert 2+2, 4 would pass.
#
# assert 2+2, 5 would fail and print:
#
# assert 2+2, 5 failed. Got 4.
def assert(got, expected)
  DEFAULT_TESTER.assert(got, expected)
end

# Print counts of all assertions: how many there were, how many passed, and how many failed.
# Then, reset the counts
def count_assertions
  DEFAULT_TESTER.count_assertions
end


# should pass
#assert 2+2, 4
#assert "lemons", 'lemons'
#assert 3.4, 3.4000002

# should fail
#assert 2+2, 5
#assert 1.23, 1.24
