# -*- Mode: ruby; indent-tabs-mode: nil -*-
#
#  $Id: tc_expander.rb,v 1.18 2004/02/06 05:54:51 hisa Exp $
#
#  Copyright (c) 2003 FUJIMOTO Hisakuni <hisa@fobj.com>
#

require 'test/unit'
require 'tempura/expander'
require 'tempura/charconv'
require 'rexml/document'
require 'model'

class TC_Expander < Test::Unit::TestCase

  def setup
    str_en = File.open("view.en.html") { |f| f.read }
    str_ja = File.open("view.ja.html") { |f| f.read }
    @doc_en = REXML::Document.new(Tempura::CharConvDefault.to_u8(str_en))
    @doc_ja = REXML::Document.new(Tempura::CharConvEUC.to_u8(str_ja))
    @doc = @doc_en
    @model_en = Model.new( [[ 'ebiten', 32 ], [ 'nasu', 12 ], [ 'kakiage', 8 ]] )
    @model_ja = Model.new( [[ '³¤Ï·Å·', 32 ], [ '²Ø»Ò', 12 ], [ '¤«¤­ÍÈ¤²', 8 ]] )
    @input_xml = "expander_test.xml"
  end

  def test_s_pre_expand
    assert_nothing_raised { Tempura::Expander.pre_expand(@doc_en) }
    assert_nothing_raised { Tempura::Expander.pre_expand(@doc_ja) }
    src = "<hoge _nil_=''/>"
    doc = REXML::Document.new(src)
    ret = nil
    assert_nothing_raised { ret = Tempura::Expander.pre_expand(doc) }
    assert_equal( "", ret.to_s )
    assert_equal( src, doc.to_s )
    assert_not_same( doc, ret )
    ret = nil
    assert_nothing_raised { ret = Tempura::Expander.pre_expand(doc, true) }
    assert_equal( "", ret.to_s )
    assert_equal( "", doc.to_s )
    assert_same( doc, ret )
  end

  def test_pre_expand
    exp = Tempura::Expander.new
    assert_nothing_raised { exp.pre_expand(@doc_en) }
    assert_nothing_raised { exp.pre_expand(@doc_ja) }
    src = "<hoge _nil_=''/>"
    doc = REXML::Document.new(src)
    ret = nil
    assert_nothing_raised { ret = exp.pre_expand(doc) }
    assert_equal( "", ret.to_s )
    assert_equal( src, doc.to_s )
    assert_not_same( doc, ret )
    ret = nil
    assert_nothing_raised { ret = exp.pre_expand(doc, true) }
    assert_equal( "", ret.to_s )
    assert_equal( "", doc.to_s )
    assert_same( doc, ret )
  end

  def test_s_new
    exp = nil
    assert_nothing_raised { exp = Tempura::Expander.new }
    assert_kind_of( Tempura::Expander, exp )
    assert_nothing_raised { exp = Tempura::Expander.new(2) }
    assert_kind_of( Tempura::Expander, exp )
  end

  def test_safe_level
    assert_equal( $SAFE_LEVEL, Tempura::Expander.new.safe_level )
    (0..5).each do |level|
      assert_equal( level, Tempura::Expander.new(level).safe_level )
    end
  end

  def test_default_action
    exp = Tempura::Expander.new
    assert_nothing_raised { exp.default_action }
    assert_equal( "", exp.default_action )
  end

  def test_set_default_action
    exp = Tempura::Expander.new
    assert_nothing_raised { exp.default_action = "myapp.cgi" }
    assert_equal( "myapp.cgi", exp.default_action )
    doc = REXML::Document.new '<a href="" _event_="show//msg==>%(hello)">hello</a>'
    exp.expand( nil, doc, nil, true )
    href = doc.root.attributes['href']
    assert_match( /^myapp.cgi?/, href )
    assert_match( /\?.*\bevent=show\b/, href )
    assert_match( /\?.*\bmsg=hello\b/, href )
  end

  def test_default_event_key
    exp = Tempura::Expander.new
    assert_nothing_raised { exp.default_event_key }
    assert_equal( "event", exp.default_event_key )
  end

  def test_set_default_event_key
    exp = Tempura::Expander.new
    assert_nothing_raised { exp.default_event_key = "command" }
    assert_equal( "command", exp.default_event_key )
    doc = REXML::Document.new '<a href="" _event_="show//msg==>%(hello)">hello</a>'
    exp.expand( nil, doc, nil, true )
    href = doc.root.attributes['href']
    assert_match( /\?.*\bcommand=show\b/, href )
    assert_match( /\?.*\bmsg=hello\b/, href )
  end

  def test_expand
    exp = Tempura::Expander.new
    ret = nil
    model = @model_en
    assert_nothing_raised { ret = exp.expand(binding, @doc_en, nil) }
    assert_kind_of( REXML::Element, ret )
    assert_not_same( ret, @doc_en )

    model = @model_ja
    assert_nothing_raised { ret = exp.expand(binding, @doc_ja, Tempura::CharConvEUC) }
    assert_kind_of( REXML::Element, ret )
    assert_not_same( ret, @doc_ja )

    model = @model_en
    assert_nothing_raised { ret = exp.expand(binding, @doc_en, nil, true) }
    assert_kind_of( REXML::Element, ret )
    assert_same( ret, @doc_en )

    model = @model_ja
    assert_nothing_raised { ret = exp.expand(binding, @doc_ja, Tempura::CharConvEUC, true) }
    assert_kind_of( REXML::Element, ret )
    assert_same( ret, @doc_ja )
  end

  def test_expand_to_string
    exp = Tempura::Expander.new
    ret = nil
    model = @model_en
    assert_nothing_raised { ret = exp.expand_to_string(binding, @doc_en, nil) }
    assert_kind_of( String, ret )

    model = @model_ja
    assert_nothing_raised { ret = exp.expand_to_string(binding, @doc_ja, Tempura::CharConvEUC) }
    assert_kind_of( String, ret )
  end

  def test_set_param_filter
    t = Time.now
    exp = Tempura::Expander.new
    assert_nothing_raised do
      exp.param_filter do |evt,param,bind|
        param = exp.default_param_filter(evt, param, bind)
        param['time'] = t.to_s
        param
      end
    end
    doc = REXML::Document.new("<form _event_='hoge'/>")
    exp.expand(nil, doc, nil, true)
    elm = doc.elements["form/input[@name='time']"]
    assert_kind_of( REXML::Element, elm )
    assert_equal( t.to_s, elm.attributes['value'] )

    doc = REXML::Document.new "<a href='dummy' _event_='hoge'>hoge</a>"
    exp.expand(nil, doc, nil, true)
    val = doc.root.attributes['href']
    val = val.split('?')[1..-1].join.split(';').map{ |i| i.split('=') }.find{|i| i.first == 'time' }
    assert_equal( exp.url_encode(t.to_s), val.last )
  end

  def test_set_event_filter
    exp = Tempura::Expander.new
    assert_nothing_raised do
      exp.action_filter { 'hogehoge' }
    end
    doc = REXML::Document.new "<form _event_='hoge'/>"
    exp.expand(nil, doc, nil, true)
    elm = doc.elements["form"]
    assert_kind_of( REXML::Element, elm )
    assert_equal( 'hogehoge', elm.attributes['action'] )

    doc = REXML::Document.new "<a href='dummy' _event_='hoge'>hoge</a>"
    exp.expand(nil, doc, nil, true)
    val = doc.root.attributes['href']
    assert_equal( 'hogehoge', val.split('?')[0] )
  end

  def test__nil_
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _nil_=''/>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "", doc.to_s )
    doc = REXML::Document.new "<hoge _nil_='Time.now'/>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "", doc.to_s )
  end

  def test__child_
    # an object
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _child_='t'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( t.to_s, doc.root.text )

    # an instance of REXML::Child
    doc = REXML::Document.new "<hoge _child_='child'/>"
    child = REXML::Element.new "child"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    elm = doc.elements['hoge/child']
    assert_kind_of( REXML::Element, elm )
    assert_equal( 'child', elm.name )

    # false => do nothing
    doc = REXML::Document.new "<hoge _child_='false'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "<hoge>hoge hoge</hoge>", doc.to_s )

    # nil => remove self
    doc = REXML::Document.new "<hoge _child_='nil'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "", doc.to_s )

    # nil => remove (2)
    doc = REXML::Document.new "<doc><hoge _child_='nil'>hoge hoge</hoge></doc>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( REXML::Element.new('doc').to_s, doc.to_s )

    # nest
    t = Time.now
    doc = REXML::Document.new "<hoge _child_='false'><time _child_='t'/></hoge>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    elm = doc.elements['hoge/time']
    assert_kind_of( REXML::Element, elm )
    assert_equal( t.to_s, elm.text )

    # xml string
    xmlstr = "<message>hello world</message>"
    doc = REXML::Document.new "<hoge _child_='xmlstr'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<hoge>&lt;message&gt;hello world&lt;/message&gt;</hoge>", doc.to_s )
  end

  def test__child_xml_
    # an object
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _child_xml_='t'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( t.to_s, doc.root.text )

    # an instance of REXML::Child
    doc = REXML::Document.new "<hoge _child_xml_='child'/>"
    child = REXML::Element.new "child"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<hoge><child/></hoge>", doc.to_s )

    # false => do nothing
    doc = REXML::Document.new "<hoge _child_xml_='false'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "<hoge>hoge hoge</hoge>", doc.to_s )

    # nil => remove self
    doc = REXML::Document.new "<hoge _child_xml_='nil'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "", doc.to_s )

    # nil => remove (2)
    doc = REXML::Document.new "<doc><hoge _child_xml_='nil'>hoge hoge</hoge></doc>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( REXML::Element.new('doc').to_s, doc.to_s )

    # nest
    t = "<hoge>#{Time.now}</hoge>"
    doc = REXML::Document.new "<hoge _child_xml_='false'><time _child_xml_='t'/></hoge>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<hoge><time>#{t}</time></hoge>", doc.to_s )

    # xml string
    xmlstr = "<message>hello world</message>"
    doc = REXML::Document.new "<hoge _child_xml_='xmlstr'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<hoge><message>hello world</message></hoge>", doc.to_s )
  end

  def test__self_
    # an object
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<doc><hoge _self_='t'/></doc>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( t.to_s, doc.root.text )

    # an instance of REXML::Child
    doc = REXML::Document.new "<doc><hoge _self_='child'/></doc>"
    child = REXML::Element.new "child"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<doc><child/></doc>", doc.to_s )

    # false => do nothing
    doc = REXML::Document.new "<hoge _self_='false'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "<hoge>hoge hoge</hoge>", doc.to_s )

    # nil => remove self
    doc = REXML::Document.new "<hoge _self_='nil'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "", doc.to_s )

    # nil => remove (2)
    doc = REXML::Document.new "<doc><hoge _self_='nil'>hoge hoge</hoge></doc>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( REXML::Element.new('doc').to_s, doc.to_s )

    # nest
    t = Time.now
    doc = REXML::Document.new "<hoge _self_='false'><time _self_='t'/></hoge>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<hoge>#{t}</hoge>", doc.to_s )

    # xml string
    xmlstr = "<message>hello world</message>"
    doc = REXML::Document.new "<doc><hoge _self_='xmlstr'/></doc>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<doc>&lt;message&gt;hello world&lt;/message&gt;</doc>", doc.to_s )
  end

  def test__self_xml_
    # an object
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<doc><hoge _self_xml_='t'/></doc>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<doc>#{t}</doc>", doc.to_s )

    # an instance of REXML::Child
    doc = REXML::Document.new "<hoge _self_xml_='child'/>"
    child = REXML::Element.new "child"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<child/>", doc.to_s )

    # false => do nothing
    doc = REXML::Document.new "<hoge _self_xml_='false'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "<hoge>hoge hoge</hoge>", doc.to_s )

    # nil => remove self
    doc = REXML::Document.new "<hoge _self_xml_='nil'>hoge hoge</hoge>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( "", doc.to_s )

    # nil => remove (2)
    doc = REXML::Document.new "<doc><hoge _self_xml_='nil'>hoge hoge</hoge></doc>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( REXML::Element.new('doc').to_s, doc.to_s )

    # nest
    t = "<hoge>#{Time.now}</hoge>"
    doc = REXML::Document.new "<hoge _self_xml_='false'><time _self_xml_='t'/></hoge>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<hoge>#{t}</hoge>", doc.to_s )

    # xml string
    xmlstr = "<message>hello world</message>"
    doc = REXML::Document.new "<hoge _self_xml_='xmlstr'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    assert_equal( "<message>hello world</message>", doc.to_s )
  end

  def test__if_
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _if_='bool'>begin<span _child_='t'/>end</hoge>"
    ret = nil

    # result was true
    bool = true
    assert_nothing_raised { ret = exp.expand(binding, doc, nil) }
    assert_equal( "begin#{t}end", ret.to_s )

    # result was false
    bool = false
    assert_nothing_raised { ret = exp.expand(binding, doc, nil) }
    assert_equal( "", ret.to_s )
  end

  def test__unless_
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _unless_='bool'>begin<span _child_='t'/>end</hoge>"
    ret = nil

    # result was true
    bool = true
    assert_nothing_raised { ret = exp.expand(binding, doc, nil) }
    assert_equal( "", ret.to_s )

    # result was false
    bool = false
    assert_nothing_raised { ret = exp.expand(binding, doc, nil) }
    assert_equal( "begin#{t}end", ret.to_s )
  end

  def test__attr_
    t = Time.now
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _attr_time='t' _attr_str='%(hello world)' _attr_version='VERSION'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    attr = doc.root.attributes
    assert_equal( t.to_s, attr['time'] )
    assert_equal( 'hello world', attr['str'] )
    assert_equal( VERSION, attr['version'] )
  end

  def test__attr_2
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<hoge _attr_hoge='nil' hoge='hoge'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    attr = doc.root.attributes
    assert_nil(doc.root.attributes['hoge'])
    doc = REXML::Document.new "<hoge _attr_hoge='false' hoge='hoge'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    attr = doc.root.attributes
    assert_equal('hoge', doc.root.attributes['hoge'])
    doc = REXML::Document.new "<hoge _attr_hoge='%(hello)' hoge='hoge'/>"
    assert_nothing_raised { exp.expand(binding, doc, nil, true) }
    attr = doc.root.attributes
    assert_equal('hello', doc.root.attributes['hoge'])
  end

  def test__event_
    exp = Tempura::Expander.new

    doc = REXML::Document.new "<form _event_='hoge'/>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    elm = doc.elements["form"]
    assert_kind_of( REXML::Element, elm )
    assert_equal( '', elm.attributes['action'] )
    elm = elm.elements["input[@name='#{exp.default_event_key}']"]
    assert_equal( 'hoge', elm.attributes['value'] )

    doc = REXML::Document.new "<a href='dummy' _event_='hoge'>hoge</a>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    val = doc.root.attributes['href']
    action, params = val.split('?')
    assert_equal( '', action )
    params = params.split(';').map{|i|i.split('=')}
    assert_equal( 'hoge', params.assoc(exp.default_event_key).last )

    input_param = { 'arg1' => 111, 'arg2' => 222, 'arg3' => 333 }
    input_param_str = input_param.map{ |key,val| "#{key}==>#{val}" }.join('//')
    doc = REXML::Document.new "<form _event_='hoge//#{input_param_str}'/>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    input_param.each do |key,val|
      elm = doc.elements["form/input[@name='#{key}']"]
      assert_kind_of( REXML::Element, elm )
      assert_equal( val.to_s, elm.attributes['value'] )
    end

    input_param = { 'arg1' => '111', 'arg2' => '222', 'arg3' => '333' }
    input_param_str = input_param.map{ |key,val| "#{key}==>#{val}" }.join('//')
    doc = REXML::Document.new "<a href='dummy' _event_='hoge//#{input_param_str}'>hoge</a>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    val = doc.root.attributes['href']
    output_action, output_param_str = val.split('?')
    assert_equal( exp.default_action, output_action )
    output_param = {}
    output_param_str.split(';').each do |i|
      key,val = i.split('=')
      output_param[key] = val unless key == exp.default_event_key
    end
    assert_equal( input_param, output_param )

    # _action_
    doc = REXML::Document.new "<form _event_='hoge' _action_='alt.cgi'/>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( 'alt.cgi', doc.elements["form"].attributes['action'] )
    doc = REXML::Document.new "<a _event_='hoge' _action_='alt.cgi'/>"
    assert_nothing_raised { exp.expand(nil, doc, nil, true) }
    assert_equal( 'alt.cgi?event=hoge', doc.elements["a"].attributes['href'] )
  end

  def test__block_
    exp = Tempura::Expander.new
    doc = REXML::Document.new "<list>\n<item _block_='model.items//each//item' _attr_name='item.name' _child_='item.price'/>\n</list>\n"
    ret = nil

    model = @model_en
    assert_nothing_raised { ret = exp.expand( binding, doc, nil ) }
    model.items.each do |item|
      elm = ret.elements["list/item[@name='#{item.name}']"]
      assert_equal( item.price, elm.text.to_i )
    end

    model = @model_ja
    assert_nothing_raised { ret = exp.expand( binding, doc, Tempura::CharConvEUC ) }
    model.items.each do |item|
      u8name = Tempura::CharConvEUC.to_u8(item.name)
      elm = ret.elements["list/item[@name='#{u8name}']"]
      assert_equal( item.price, elm.text.to_i )
    end

    doc = REXML::Document.new "<list>\n<item _block_='model.items//each//item' _attr_name='item.name'><price _child_='item.price'/></item>\n</list>\n"
    ret = nil
    model = @model_en
    assert_nothing_raised { ret = exp.expand( binding, doc, nil ) }
    model.items.each do |item|
      elm = ret.elements["list/item[@name='#{item.name}']/price"]
      assert_equal( item.price, elm.text.to_i )
    end
  end

  def test_expander_test_xml
    exp = Tempura::Expander.new
    doc = REXML::Document.new(File.open(@input_xml))
    exp.expand( nil, doc, nil, true )
    result = doc.root.to_s.to_a
    re = /^_([^_]*)_:(.*)$/
    while line = result.shift do
      if ma = re.match(line) and ma[2] then
        val_expectation = ma[2].strip
        val_result = result.shift.strip
        assert_equal( val_expectation, val_result, "test_xml : #{ma[1]}" )
      end
    end
  end

end
