#!/usr/bin/env ruby
# This is a test for FormatR it uses perl and compares output from perl and ruby
# If you want to keep the output to look at pass in -keep or --keep

$VERBOSE = true

require 'runit/testcase'
require 'runit/cui/testrunner'
require 'runit/testsuite'

require "format.rb"
include FormatR

$page_number = -1

## These formats are defined before the class as the formatting gets
## tricky in the face of indentation using the here documents. You can get around
## this by passing in an array of strings as shown in $test_5 below.

#test 1
# Uses numbers, comments, center, left and right alignment along
# with numbers.
$f = <<DOT   
  @###.#   @##########.###  @##.###| @.### @##.. .
#comment
  num,      num,             snum,    snum, snum
  1234567      1234567.890  -12.340| -12.3 -12.. .
left|@<<<<| right|@>>>>| |center|@||||| | num|@##.#|
     twenty_two,  twenty_two,    twenty_two,  snum
left|22   | right|   22| |center|  22   | num|-12.3|
DOT

#test 1,2
# This format is just a header with no variables.
$top = <<DOT
            Passwd File
      Name   Name   Login   Office   Uid       Gid      Home
------------------------------------------------------
DOT

#test 2 
# Tests left, right, numbers, and formats that aren't picture,
# vars, picture, vars,...
$from_perl =  <<FROM_PERL
Foo   @<<<<  @>>>>  @<<<<   @>>>>>    @##.#####   @# @.###    @##.#.
      name,  name,  login,  office,   uid,      uid, gid,   gid,
safdsdfsdfsdf
    sdfsdfsdfdfs
bar @<<<<  @>>>>  @<<<<   @>>>>>    @|||||+  +@#####+  @<<<<
    name,  name,  login,  office,   uid,      gid,     home
baz ^<<<*   *^<<<* ^<<  @<_^_<< @||| ^#.## ^##.###
    name,    name, name,login, login,  home, uid, uid
\@#\@#
uid,uid
FROM_PERL

#test assertions
$bad_format = <<DOT
 @<<< @>>
 name,
DOT

#tests 3,4,5
## New test for # and <, perl test #2
$top_ex = <<DOT
   Piggy Locations for @<< @#, @###
                     month, day, year

Number: location              toe size
-------------------------------------------
DOT

#tests 3,4,5
# This tests ~ feature
$ex = <<DOT
@)  ~   @<<<<<<<<<<<<<<<<       @#.##
num,    location,             toe_size
DOT

# a bottom format:
$bot_ex = <<BOT
-------------------------------------------
BOT

#test 6
## Examples of ^
$hat_ex = <<BOT
Format that uses the hat
@|| ^<< ^||
long, long, long
^|||| @<<<< ^|||| ^###.## ^###.## ^###.#
long, long, long, num, num, num
Run out of chars!
^|||| ^<<<< ^|||| --
short, short,short
^<<<*^<<<<*^<<<<<<<<<<<<<
long, long, long
BOT

#test 6
# an empty top format
$empty_top = <<TOP
TOP

#tests 7,8
# Format created by pushing strings onto an array.
$test_5 = ["~  non_blank: @<<<|"]
$test_5.push("non_blank")
$test_5.push("~ blank unless $blank holds something, which it doesn\'t @<<<")
$test_5.push("blank")
$test_5.push(" ~~repeat: ^<<.")
$test_5.push("string")


class FormatTester < RUNIT::TestCase
  @@keep = false

  def initialize (a,b)
    super(a,b)
    hasPerl = `/usr/bin/env perl -h`
    @hasPerl = ((hasPerl == "") ? false : true)
  end

  def FormatTester.setKeep (keep)
    @@keep = keep
  end

  def getPerlToCompare (perl)
    program, arguments  = perl.split(' ', 2)
    if (@hasPerl)
      io = IO.popen(perl)
    else #use cached values
      io = File.open("./test/" + arguments)
    end
    perl_output = io.readlines()
    io.close()
    perl_output.collect! {|x| x.chomp()}
  end

  def compareOutput (infile, perl, tail)
    perl_output = getPerlToCompare(perl)
    tail.each {|i| perl_output.push(i.chomp())} if (tail)
    File.open("#{infile}"){|file|
      lines = file.readlines
      lines.each_index {|i|
        line = lines[i].chomp!()
        if (!(line === (perl_output[i])))
          raise "testing error between \n" +
            "#{infile}>#{line}<\nperl            >#{perl_output[i]}<" 
        end
      }
      if (lines.size != perl_output.size)
        raise "testing error between #{infile} #{lines.size()} and " + 
          "#{perl} #{perl_output.size()}, not the same size" 
      else
        File.delete(infile) unless (@@keep)
      end
    }
    true
  end

  #test $f format
  def one
    twenty_two = '22'
    num = 1234567.89012455
    snum = -12.34

    fmt1 = Format.new($f)
    fmt1.setTop($top)

    File.open("format_testfile1", File::CREAT | File::WRONLY | File::TRUNC) { |file|
      fmt1.io = file
      13.times do
        fmt1.printFormatWithBinding(binding)
      end
    }

    assert(compareOutput("format_testfile1", "./format_test.pl 1", nil))
  end


  #test $top and $from_perl
  def two
    name = "Paul Rubel"
    login ="rubel@crhc.uiuc.edu"
    office = "6/301"
    uid = 124.135791357;
    
    fmt = Format.new($from_perl) 
    File.open("format_testfile2", File::CREAT | File::WRONLY | File::TRUNC) { |file|
      gid = -13
      home = "/home/rubel/"
      fmt.io = file
      fmt.setTop($top)
      20.times do
        fmt.printFormat(binding)
      end
    }
    raise "line numbering is broken #{fmt.pageNumber()}" unless (3 == fmt.pageNumber())
    assert(compareOutput("format_testfile2", "./format_test.pl 2", nil))
  end


  def test_recursive_exceptions
    fmt = Format.new($from_perl) 
    assert_exception(FormatException, "Should have thrown an exception") {
      fmt.setTop(fmt)
    }
  end


  def test_bad_format_exception
    assert_exception(FormatException, "Malformed format slipped through") {
      badFormat = Format.new($bad_format)
    }
  end

  def helper_test_three (body_fmt, file_name)
    File.open(file_name, File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      month = "Sep"
      day = 18
      year = 2001
      num = 1
      body_fmt.setPageLength(11)
      ["Market", "Home", "Eating Roast Beef", "Having None", "On the way home"].each {|location|
        toe_size = (num * 3.5)
        body_fmt.printFormat(binding)
        num += 1
      }
      assert(1 == body_fmt.pageNumber())
    }
  end

  #test setting top and middle formats at once and page numbers
  def test_three
    0.upto(1) do |x|
      body_fmt = Format.new($top_ex, $ex)
      body_fmt.useHash( x == 0 )
      helper_test_three(body_fmt, "format_testfile3-#{x}") 
      assert(compareOutput("format_testfile3-#{x}", 
                           "./format_test.pl 3", nil))
    end
  end


  def helper_test_four (body_fmt, file_name)
    File.open(file_name, File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      month = "Sep"
      day = 18
      year = 2001
      10.times do 
        num = 1
        ["Market", "Home", "Eating Roast Beef", 
          "Having None", "On the way home"].each {|location|
          toe_size = (num * 3.5)
          body_fmt.printFormat(binding)
          num += 1
        }
      end
    }
    assert(compareOutput(file_name,
                         "./format_test.pl 4", nil))
  end

  def helper_hash_test_four (body_fmt, file_name)
    h = Hash.new
    h["month"] = "Sep"
    h['day'] = 18
    h['year'] = 2001
    File.open(file_name, File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      10.times do 
        num = 1
        h["num"] = num
        ["Market", "Home", "Eating Roast Beef", 
          "Having None", "On the way home"].each {|location|
          h["location"] = location
          h["toe_size"] = (num * 3.5)
          body_fmt.printFormatWithHash(h)
          num += 1
          h["num"] = num
        }
      end
    }
    assert(compareOutput(file_name,
                         "./format_test.pl 4", nil))
  end


  # test the end of page bottom w/out calling end of pagefinishPage methods
  def test_four
    0.upto(2) do |x|
      top_fmt = Format.new($top_ex) 
      body_fmt = Format.new($ex)
      bottom_fmt = Format.new($bot_ex)
      body_fmt.useHash(x == 0)
      body_fmt.setTop($top_ex)
      body_fmt.setBottom(bottom_fmt)
      body_fmt.setPageLength(10)
      if (x <2)
      helper_test_four(body_fmt, "format_testfile4-#{x}") 
      else
        helper_hash_test_four(body_fmt, "format_testfile4-#{x}") 
      end
    end
  end


  #test the finishPage methods and setting all three formats at once,
  #top, bottom, and middle.

  def helper_test_five (body_fmt, file_name)
    File.open(file_name,
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      month = "Sep"
      day = 19
      year = 2001
      count = 0
      10.times do 
        count += 1
        num = 1
        ["Market", "Home", "Eating Roast Beef", "Having None", "On the way home"].each {|location|
          toe_size = 1
          body_fmt.printFormat(binding)
          num += 1
        }
        body_fmt.finishPageWithFF(binding) unless (count == 10)
      end
      body_fmt.finishPageWithoutFF(binding)
    }
  end
  
  def test_five
    0.upto(1) do |x|
      body_fmt = Format.new($top_ex, $ex, $bot_ex)   
      body_fmt.useHash(x == 0)
      body_fmt.setPageLength(20)
      helper_test_five(body_fmt, "format_testfile5-#{x}")
      assert(compareOutput("format_testfile5-#{x}",
                           "./format_test.pl 5", nil))
    end
  end


  def helper_test_six (body_fmt, file_name)
    File.open(file_name,
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      long = "01234567890abcde fg  h i jklmn12345678901234567890123456.789"
      num = 123.456
      short = '123.456'
      body_fmt.printFormat(binding)
      raise "page numbering not working " unless (1 == body_fmt.pageNumber())
    }
  end

  def test_six
    0.upto(1) do |x|
      hat_top = Format.new($empty_top)
      hat_format = Format.new($hat_ex)
      hat_format.setTop(hat_top)
      hat_format.useHash(x == 1)
      helper_test_six(hat_format, "format_testfile6-#{x}")
      assert(compareOutput("format_testfile6-#{x}", 
                           "./format_test.pl 6", nil))
    end
  end


  def helper_test_seven (body_fmt, file_name)
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      blank = ""
      non_blank = "  "
      string = "abcdefghijklmn"
      body_fmt.printFormat(binding)
    }
  end
  
  def test_seven
    0.upto(1) do |x|
      tilde_format = Format.new($test_5)
      helper_test_seven(tilde_format, "format_testfile7-#{x}")
      assert(compareOutput("format_testfile7-#{x}", 
                           "./format_test.pl 7", nil))
    end
  end

  def helper_test_eight (body_fmt, file_name)
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      blank = ""
      non_blank = "  "
      string =("a"*100) + "b"

      body_fmt.printFormat(binding)
      body_fmt.finishPageWithoutFF(binding)
    }
  end

  def test_eight
    #Problems when formats don't fit on a single page!
    0.upto(1) do |x|
      tilde_format = Format.new($test_5)
      tilde_format.setPageLength(10)    
      tilde_format.useHash(x == 0)
      helper_test_eight(tilde_format, "format_testfile8-#{x}")
      assert(compareOutput("format_testfile8-#{x}", 
                           "./format_test.pl 8", nil))
    end
  end


  def helper_test_empty_top_bottom (body_fmt, file_name)
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      blank = ""
      non_blank = "  "
      string = ("a"*100) + "b"
      body_fmt.printFormat(binding)
      body_fmt.finishPageWithoutFF(binding)
    }
  end

  #can we pass in an emptry string as a top and nil as a bottom?
  def test_empty_top_bottom
    0.upto(1) do |x|
      #Problems when formats don't fit on a single page!
      tilde_format = Format.new("",$test_5, nil)
      tilde_format.useHash(x == 0)      
      tilde_format.setPageLength(10)    
      helper_test_empty_top_bottom (tilde_format, "format_testfile9-#{x}")
      assert(compareOutput("format_testfile9-#{x}", 
                           "./format_test.pl 8", nil))
    end
  end

  def helper_test_scientific (body_fmt, file_name)
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      int = 12
      exp = 1.234e-14
      mid = 123.4567E23
      big = 123.4567E200
      little = 12.34e-20
      body_fmt.printFormat(binding)
    }
  end

  def test_scientific
    f = [];
    f.push("1 @.##G### @.##e###")
    f.push("exp, exp")
    
    f.push("2 @##.###g### @##.#E###")
    f.push("big, big")
    
    f.push("3 @.#G## @.#e##")
    f.push("little, little")
    
    f.push("4 @E##")
    f.push("mid")
    
    f.push("5 @.#E## @e##")
    f.push("little, mid")
    
    f.push("6 int @.#g## @.#e##")
    f.push("int, int")
    
    f.push("7 int @.G## @.E##")
    f.push("int, int")
    0.upto(1) do |x|
      exp_format = Format.new("", f, nil)
      exp_format.useHash(x == 0)
      helper_test_scientific(exp_format, "format_testfile10-#{x}")
      assert(compareOutput("format_testfile10-#{x}", 
                           "./format_test.pl 10", nil))      
    end
  end

  def helper_test_eleven (body_fmt, file_name)
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      one = 1
      body_fmt.printFormat(binding)
    }
    assert(compareOutput(file_name,
                         "./format_test.pl 11", nil))
  end

  def helper_hash_test_eleven (body_fmt, file_name)
    h = Hash.new
    h['one'] = 1
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      body_fmt.printFormatWithHash( h )
    }
    assert(compareOutput(file_name,
                         "./format_test.pl 11", nil))
  end

  def test_eleven
    #Problems when formats don't fit on a single page!
    f = []
    f.push( '<?xml version="1.0"?>' )
    f.push( '@@@ @@@' )
    f.push( 'one,one,one,one,one,one')
    0.upto(1) do |x|
      format = Format.new(f)
      helper_test_eleven(format, "format_testfile11-#{x}")      

    end
    format = Format.new(f)
    helper_hash_test_eleven(format, "format_testfile11-2")

  end

  def helper_test_twelve (body_fmt, file_name)
    var_one, var_two, var_three, var_four = 1, 2, 3, 4.3
    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      body_fmt.printFormat(binding) 
    }
    output = []
    File.open( file_name ){ |file|
      output = file.readlines()
    }
    
    reader = FormatReader.new (body_fmt)
    res = reader.readFormat (output)
    
    #since we're keying on just @ we can't tell class without the binding
    assert (res['var_one'] == var_one.to_s)
    assert (res['var_two'] == var_two.to_s)
    assert (res['var_three'] == var_three.to_s)
    assert (res['var_four'] == var_four)
    # we need to compare last since compare may delete the file when its done
    assert(compareOutput(file_name, 
                         "./format_test.pl 12", nil))
  end

  # try out reading in variables
  def test_twelve
    puts "test 12"
    f = []
    f.push( '<?xml version="1.0"?>' )
    f.push( '@@@ Paul @@@ }Rubel @< @|| @#.#' )
    f.push( 'var_one,var_one,var_one,var_one,var_one,var_one,' +
           ' var_two, var_three, var_four')
    f.push( '@<<< @<<<')
    f.push( 'var_one,var_one')
    0.upto(1) do |x|
      format = Format.new(f)
      format.useHash(x == 0)      
      pictures = format.getPictureLines
      assert(pictures.size == 3)

      helper_test_twelve(format, "format_testfile12-#{x}")
    end
  end

  def helper_test_thirteen (body_fmt, file_name)
    one = "FIVE SIX"
    val1 = one.clone
    two = "ONE TWO"
    val2 = two.clone
    val3 = "Yay"
    var_exp = 3.44e10

    File.open(file_name, 
              File::CREAT | File::WRONLY | File::TRUNC) { |file|
      body_fmt.io = file
      body_fmt.printFormat(binding) 
    }

    #save the output for later
    output = []
    File.open( file_name ){ |file|
      output = file.readlines()
    }
    
    reader = FormatReader.new (body_fmt)
    res = reader.readFormat (output)
    #since we're keying on just @ we can't tell class without the binding
    assert (res['val1'] == one)
    assert (res['val2'] == two)
    assert (res['val3'] == val3)
    assert (res['var_exp'] == var_exp)
  end

  # See if we can handle repeat lines in with regular ones
  def test_thirteen
    puts "test 13"
    f = []
    f.push( '<?xml version="1.0"?>' )
    f.push( '~~^<<< TEST1' )
    f.push( 'val1' )
    f.push( '~~^<< TEST2' )
    f.push( 'val2' )
    f.push( '@<< @<<' )
    f.push('val3, val3')
    f.push('@##.##e##')
    f.push('var_exp')

    format = Format.new(f)
    0.upto(1) do |x|
      format.useHash(x == 0)
      helper_test_thirteen(format, "format_testfile13-#{x}")
    end

  end
  
  # show that page numbers are fixed on the top and bottom
  def test_page_numbers
    #Problems when formats don't fit on a single page!
    File.open("format_testfile14", File::CREAT | File::WRONLY | File::TRUNC) { |file|
      top = "---- Top --- Page Number @<<<----\n$page_number\n"
      bottom = "---- Bot --- Page Number @<<<----\n$page_number\n \n"
      page_num_format = Format.new(top,$ex, bottom)
      page_num_format.io = file
      page_num_format.setPageLength(10)    
      location = "Who Knows"
      203.times do 
        $page_number = page_num_format.pageNumber()
        num = page_num_format.pageNumber()
        toe_size = page_num_format.pageNumber() * 1.0
        page_num_format.printFormat(binding)
        
        $page_number = page_num_format.pageNumber()
        num = page_num_format.pageNumber()
        toe_size = page_num_format.pageNumber()
      end
    }
    assert(compareOutput("format_testfile14", "./format_test.pl 13", nil))
  end


end 

keep = (ARGV.include?("-keep") || ARGV.include?("--keep"))
FormatTester.setKeep(keep)
suite = FormatTester.suite
RUNIT::CUI::TestRunner.run(suite)

