#!/usr/bin/env ruby

# == cli-test
# epcb-csv.{rb,exe}のコマンドラインインタフェースのテスト。
#
# Revision:: $Id$
#
require 'optparse'
require 'ostruct'
require 'fileutils'
require 'epcb/csv/all-generator'
require 'epcb/test/cli/common'

$KCODE = "s"

parent_dir = File.expand_path(File.dirname($0))
$:.push(parent_dir)

$options = OpenStruct.new
$options.data_dir = File.join(File.dirname(File.dirname(parent_dir)), "test/data")
$options.program = "ruby epcb-csv.rb"
$options.remove_std = false
$options.tmp_dir = File.join(File.dirname(File.dirname(parent_dir)), "tmp")
$options.tmp_dir = "." unless FileTest.directory?($options.tmp_dir)

helper = nil
ARGV.options do |opts|
  helper = EPCB::Test::CLI::Helper.new($options)

  opts.banner = <<BANNER
Usage: #{File.basename($0)} [options]
BANNER
  opts.separator("")
  opts.separator("options are:")
  # --data-dir
  helper.define_data_dir(opts)
  # --tmp-dir
  helper.define_tmp_dir(opts)
  # --program
  helper.define_program(opts)
  # --remove
  helper.define_remove(opts)
  # --help
  helper.define_help(opts)

  # オプションの解析
  opts.parse!
end

dir = File.expand_path($options.tmp_dir)
$out = File.open("#{dir}/stdout", "w")
$err = File.open("#{dir}/stderr", "w")

# リダイレクトしたファイルを削除する処理を登録
helper.register_at_exit([ $out, $err ])

# test/unitはat_exitを使用するので、ここでrequire
require 'test/unit'
require 'epcb/test/cli/common'

class EPCB::Test::CLI::TestData
  attr_reader :_1_0_simple_data, :_1_0_simple_master_data, :_1_0_3sheets_data, \
  :_1_1_0_2sheets_data, :_1_1_0_2sheets_data

  def initialize
    @_1_0_simple_data = [
      ["$$2004年10月"],
      ["2004/10/1","音楽","-14000",""],
      ["","食費","-105",""],
      ["","煙草","-270",""],
      ["2004/10/2","娯楽","-2800",""],
      ["","食費","-270",""],
      ["","煙草","-270",""],
      ["2004/10/3","煙草","-270",""],
      ["","食費","-545",""],
      ["","食費","-130",""],
      ["","理髪代","-8820",""],
      ["","日用品","-1029",""],
      ["","食費","-377",""],
      ["","煙草","-270",""],
    ]

    @_1_0_simple_master_data = [
      "用途マスタ","食費","交通費","交際費","被服費","娯楽","教育"
    ]

    @_1_0_3sheets_data = [
      ["$$2004年8月"],
      ["2004/8/1","娯楽","-1279",""],
      ["","食費","-315",""],
      ["","被服費","-4095",""],
      ["","被服費","-945",""],
      ["","煙草","-270",""],
      ["","化粧品","-1525",""],
      ["2004/8/2","食費","-204",""],
      ["","食費","-147",""],
      ["","煙草","-270",""],

      ["$$2004年9月"],
      ["2004/9/1","煙草","-540",""],
      ["","食費","-100",""],
      ["","食費","-1300",""],
      ["2004/9/2","食費","-520",""],
      ["","食費","-262",""],

      ["$$2004年10月"],
      ["2004/10/1","音楽","-14000",""],
      ["","食費","-105",""],
      ["","煙草","-270",""],
      ["2004/10/2","娯楽","-2800",""],
      ["","食費","-270",""],
      ["","煙草","-270",""],
    ]

    @_1_1_0_2sheets_data = [
      ["$$2005年2月"],
      ["2005/2/1","娯楽","-1500","2/1の備考"],
      ["","煙草","-270",""],
      ["","医薬品","-853",""],
      ["2005/2/2","食費","-231",""],
      ["","煙草","-270",""],
      ["","交通費","-500","品川<->横浜"],
      ["2003/2/3","家電製品","-40000",""],
      ["","交通費","-320",""],
      ["","理髪代","-5250",""],

      ["$$2005年3月"],
      ["2005/3/15","本・雑誌","-1500",""],
      ["","音楽","-3000",""],
    ]
  end
end

#
# 1.0 -> 1.1のテストケース。
#
class EPCB::Test::CLI::TestEPCBCSVError < Test::Unit::TestCase
  def initialize(name)
    super(name)
    @helper = EPCB::Test::CLI::TestCaseHelper.new($out, $err, $options.remove_std)
    @data = EPCB::Test::CLI::TestData.new
  end

  def test_arg_error
    expected = 110
    # サブコマンドなし
    @helper.putil.quiet_run($options.program, __FILE__, __LINE__)
    assert_equal(expected, @helper.putil.exit_status)

    # 不正なサブコマンド
    @helper.putil.quiet_run("#{$options.program} hoge", __FILE__, __LINE__)
    assert_equal(expected, @helper.putil.exit_status)

    # src_fileなし
    @helper.putil.quiet_run("#{$options.program} gencsv", __FILE__, __LINE__)
    assert_equal(expected, @helper.putil.exit_status)
  end

  def test_gencsv_error
    # src_fileが存在しない
    @helper.putil.quiet_run("#{$options.program} gencsv not_found.xls",
                            __FILE__, __LINE__)
    assert_equal(121, @helper.putil.exit_status)

    # -tで指定されたファイルが存在しない
    @helper.putil.quiet_run("#{$options.program} genall" \
                            " #{$options.data_dir}/1_0_simple.xls" \
                            " -t #{$options.data_dir}/not_found.xls" \
                            " -o #{$options.tmp_dir}/test_unknown_version.xls",
                            __FILE__, __LINE__)
    assert_equal(121, @helper.putil.exit_status)
    
    # -oで指定されたファイルが読取専用(Errno::EACCESS)
    @helper.putil.quiet_run("#{$options.program} gencsv" \
                            " #{$options.data_dir}/1_0_simple.xls" \
                            " -o #{$options.data_dir}/read_only.csv",
                            __FILE__, __LINE__)
    assert_equal(120, @helper.putil.exit_status)

    # 上記のgenall版
    @helper.putil.quiet_run("#{$options.program} genall" \
                            " #{$options.data_dir}/1_0_simple.xls" \
                            " -t #{$options.data_dir}/ExcelPettyCashBook-1.1.1.xls" \
                            " -o #{$options.data_dir}/read_only.xls",
                            __FILE__, __LINE__)
    assert_equal(120, @helper.putil.exit_status)

    # -oで指定されたパスが存在しない(Errno::ENOENT)
    @helper.putil.quiet_run("#{$options.program} gencsv" \
                            " #{$options.data_dir}/1_0_simple.xls" \
                            " -o #{$options.data_dir}/no_path/read_only.csv -V",
                            __FILE__, __LINE__)
    assert_equal(121, @helper.putil.exit_status)

    # src_fileがディレクトリ
    @helper.putil.quiet_run("#{$options.program} gencsv #{File.dirname($0)}",
                            __FILE__, __LINE__)
    assert_equal(123, @helper.putil.exit_status)

    # 解析するCSVファイルのカラム数が不正
    @helper.putil.quiet_run("#{$options.program} genexcel" \
                            " #{$options.data_dir}/illegal_column_size.csv" \
                            " -t #{$options.data_dir}/ExcelPettyCashBook-1.1.1.xls" \
                            " -o #{$options.tmp_dir}/illegal_column_size.xls",
                            __FILE__, __LINE__)
    assert_equal(130, @helper.putil.exit_status)

    # genallで-tが指定されていない
    @helper.putil.quiet_run("#{$options.program} genexcel" \
                            " #{$options.data_dir}/illegal_column_size.csv" \
                            " -o #{$options.tmp_dir}/illegal_column_size.xls -V",
                            __FILE__, __LINE__)
    assert_equal(111, @helper.putil.exit_status)

    # -Sのバージョンが不正
    @helper.putil.quiet_run("#{$options.program} gencsv -S 10.10.10" \
                            " #{$options.data_dir}/nodata.xls",
                            __FILE__, __LINE__)
    assert_equal(110, @helper.putil.exit_status)

    # -tで指定されたExcelPettyCashBookのバージョンが不正
    @helper.putil.quiet_run("#{$options.program} genall" \
                            " #{$options.data_dir}/1_0_simple.xls" \
                            " -t #{$options.data_dir}/unknown_version.xls" \
                            " -o #{$options.tmp_dir}/test_unknown_version.xls -V",
                            __FILE__, __LINE__)
    assert_equal(140, @helper.putil.exit_status)
  end
end

#
# 1.0 -> 1.1のテストケース。
#
class EPCB::Test::CLI::TestEPCBCSV_1_0 < Test::Unit::TestCase

  def initialize(name)
    super(name)
    @helper = EPCB::Test::CLI::TestCaseHelper.new($out, $err, $options.remove_std)
    @data = EPCB::Test::CLI::TestData.new
  end

  #
  # シート1枚のデータ(1_0_simple.xls)
  #
  def test_gencsv_1_0_simple
    @helper.gencsv_and_validate_csv("1_0_simple", @data._1_0_simple_data)

    # マスタシートもCSVに出力する場合のテスト
    expected = @data._1_0_simple_data.dup
    expected.unshift(@data._1_0_simple_master_data.dup)
    expected.unshift(["$$マスタ"])
    @helper.gencsv_and_validate_csv("1_0_simple", expected, "-m")
  end

  #
  # シート3枚のデータ(1_0_3sheets.xls)
  #
  def test_gencsv_1_0_3sheets
    @helper.gencsv_and_validate_csv("1_0_3sheets", @data._1_0_3sheets_data)
  end

  #
  # genallのテスト
  #
  def test_genall_1_0_to_1_1
    _1_0_validate_proc = Proc.new do |data, line, column|
      puts "L#{line} C#{column}: #{@helper.eutil.get_offset(line, column).value.inspect}"
      # dataとexcelでは、下記のように2番目以降のオフセットが異なるため、
      # columnが2以上のものはずらす処理を入れている
      # data : [ 日付,       用途, 金額  ]
      # excel: [ 日付, 曜日, 用途, 金額, 備考 ] 

      # 日付
      if column == 0
        next if data[line][column] == ""
        value = @helper.eutil.get_offset(line, column).value.to_s
        date = EPCB::ExcelUtil.to_yyyymmdd_format(value)
        assert_equal(data[line][column], date)
      # 用途
      elsif column == 2
        assert_equal(data[line][column-1],
                     @helper.eutil.get_offset(line, column).value.to_s)
      # 金額
      elsif column == 3
        assert_equal(data[line][column-1].to_i,
                     @helper.eutil.get_offset(line, column).value.to_i)
      # 備考(1.0から移行した場合必ずnilなはず)
      elsif column == 6
        assert_nil(@helper.eutil.get_offset(line, column).value)
      end
    end
    
    name = "1_0_simple"
    template = "ExcelPettyCashBook-1.1.0.xls"
    outfile = "#{$options.tmp_dir}/#{name}_NEW.xls"
    @helper.genall_and_validate_excel(name, template, outfile,
                                     @data._1_0_simple_data, &_1_0_validate_proc)

    # -m をつけた場合のテスト
    @helper.run_command("genall", name,
                        "-t #{$options.data_dir}/#{template} -o #{outfile} -m")
    assert(FileTest.exist?(outfile))
    dest_path = File.file_system.getAbsolutePathName(outfile)
    begin
      workbook = @helper.eutil.open_workbook_and_activate(dest_path)
      master = workbook.worksheets(3)
      for i in 0 .. @data._1_0_simple_master_data.size - 1
        assert_equal(@data._1_0_simple_master_data[i], master.cells(i + 1, 1).value.to_s)
      end
      assert_nil(master.cells(@data._1_0_simple_master_data.size + 1, 1).value)
    ensure
      @helper.eutil.destroy_excel
    end
    
    name = "1_0_3sheets"
    outfile = "#{$options.tmp_dir}/#{name}_NEW.xls"
    @helper.genall_and_validate_excel(name, template, outfile,
                                      @data._1_0_3sheets_data, &_1_0_validate_proc)
  end #test_genall_1_0_to_1_1#

  #
  # 1.1.0->1.1.1のgenallのテスト
  #
  def test_genall_1_1_0_to_1_1_1
    _1_1_validate_proc = Proc.new do |data, line, column|
      puts "L#{line} C#{column}: #{@helper.eutil.get_offset(line, column).value.inspect}"
      # dataとexcelでは、下記のように2番目以降のオフセットが異なるため、
      # columnが2以上のものはずらす処理を入れている
      # data : [ 日付,       用途, 金額  ]
      # excel: [ 日付, 曜日, 用途, 金額, 備考 ] 

      # 日付
      if column == 0
        #next if data[line][column] == ""
        value = @helper.eutil.get_offset(line, column).value.to_s
        date = EPCB::ExcelUtil.to_yyyymmdd_format(value)
        assert_equal(data[line][column], date)
      # 用途
      elsif column == 2
        assert_equal(data[line][column-1],
                     @helper.eutil.get_offset(line, column).value.to_s)
      # 金額
      elsif column == 3
        assert_equal(data[line][column-1].to_i,
                     @helper.eutil.get_offset(line, column).value.to_i)
      # 備考
      elsif column == 6
        assert_equal(data[line][column-1],
                     @helper.eutil.get_offset(line, column).value.to_s)
      end
    end
    
    name = "1_1_0_2sheets"
    template = "ExcelPettyCashBook-1.1.1.xls"
    outfile = "#{$options.tmp_dir}/#{name}_NEW.xls"
    @helper.genall_and_validate_excel(name, template, outfile,
                                      @data._1_1_0_2sheets_data, &_1_1_validate_proc)

  end #test_genall_1_1_to_1_1#

end
