#!/usr/bin/env ruby
#
# Rwv2 -- Microsoft Word Parser extension
# Copyright (C) 2003 Hannes Wyss, ywesee - intellectual capital connected
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# ywesee - intellectual capital connected
# Winterthurerstrasse 52, CH-8006 Zürich, Switzerland
# hwyss@ywesee.com
#
# TestParser -- Rwv2 -- 21.8.2003 -- hwyss@ywesee.com

$: << File.expand_path('../ext', File.dirname(__FILE__))
$: << File.expand_path('../lib', File.dirname(__FILE__))

require 'test/unit'
require 'rwv2'
require 'rwv2/handlers'

class StubInlineReplacementHandler
	attr_accessor :non_required_hyphen
	def column_break
		"c"
	end
	def hard_line_break
		"\n"
	end
	def non_breaking_hyphen
		"="
	end
	def non_breaking_space
		"_"
	end
	def tab
		"t"
	end
end
class StubIncompleteReplacementHandler
	def hard_line_break
		"\n"
	end
end
["body", "footnote", "headers", "header"].each { |tpe|
	eval <<-EOF
	class StubSubDocumentHandler
		attr_reader :#{tpe}_starts, :#{tpe}_ends
		def #{tpe}_start
			@#{tpe}_starts ||= 0
			@#{tpe}_starts += 1
		end
		def #{tpe}_end
			@#{tpe}_ends ||= 0
			@#{tpe}_ends += 1
		end
	end
	EOF
}
class StubSubDocumentHandler
	attr_reader :header_starts
	def initialize
		@header_starts = []
	end
	def header_start(header_type)
		@header_starts << header_type
	end
end
class StubTextHandler
	attr_accessor :texts, :formats, :section_properties, :section_ends
	attr_accessor :page_breaks, :paragraph_properties, :paragraph_ends
	def initialize
		@formats = []
		@texts = []
		@section_properties = []
		@paragraph_properties = []
		@section_ends = 0
		@paragraph_ends = 0
		@page_breaks = 0
	end
	def section_start(sep)
		@section_properties << sep
	end
	def section_end
		@section_ends += 1
	end
	def page_break
		@page_breaks += 1
	end
	def paragraph_start(pap)
		@paragraph_properties << pap
	end
	def paragraph_end
		@paragraph_ends += 1
	end
	def run_of_text(text, format=nil)
		@formats << format unless format.nil?
		@texts << text
	end
end
class StubTableHandler
	attr_reader :row_starts, :row_ends, :cell_starts, :cell_ends
	def initialize
		@row_starts = []
		@row_ends = 0
		@cell_starts = 0
		@cell_ends = 0
	end
	def row_start(properties=nil)
		@row_starts << properties
	end
	def row_end
		@row_ends += 1
	end
	def cell_start
		@cell_starts += 1
	end
	def cell_end
		@cell_ends += 1
	end
end

class TestRwv2Parser < Test::Unit::TestCase
	def setup
		@filename = File.expand_path('data/test.doc', File.dirname(__FILE__))
		@filename2 = File.expand_path('data/test2.doc', File.dirname(__FILE__))
		@filename3 = File.expand_path('data/test3.doc', File.dirname(__FILE__))
		@filename4 = File.expand_path('data/test4.doc', File.dirname(__FILE__))
		@filename5 = File.expand_path('data/test5.doc', File.dirname(__FILE__))
		@filename6 = File.expand_path('data/test6.doc', File.dirname(__FILE__))
		@ir_handler = StubInlineReplacementHandler.new
		@ir_handler.non_required_hyphen = "-"
	end
	def test_create_parser
		assert_nothing_raised {
			Rwv2.create_parser(@filename)
		}
	end
	def test_inline_replacement_handler
		parser = Rwv2.create_parser(@filename)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		assert_nothing_raised {
			parser.set_inline_replacement_handler(@ir_handler)
		}
		parser.parse
		expected = [
			"Paragraph 1, Standard",
			"Paragraph 2, Bold",
			"Paragraph 3, Italic",
			"Paragraph 4, Underlined",
			"Paragraph 5, Bold Italic",
			"Paragraph 6, Bold Underlined",
			"Paragraph 7, Italic Underlined",
			"Paragraph 8, Bold Italic Underlined",
			"Paragraph 9, ",
			"mixed Formats",
			"TabtTab",
			"HardLineBreak\nHardLineBreak",
			"ColumnBreakcColumnBreak",
			"NonBreakingHyphen=NonBreakingHyphen",
			"NonRequiredHyphen-NonRequiredHyphen",
			"NonBreakingSpace",
			"_",
			"NonBreakingSpace",
		]
		assert_equal(expected, handler.texts)
	end
	def test_incomplete_replacement_handler
		parser = Rwv2.create_parser(@filename)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		replacer = StubIncompleteReplacementHandler.new
		assert_nothing_raised {
			parser.set_inline_replacement_handler(replacer)
		}
		parser.parse
		expected = [
			"Paragraph 1, Standard",
			"Paragraph 2, Bold",
			"Paragraph 3, Italic",
			"Paragraph 4, Underlined",
			"Paragraph 5, Bold Italic",
			"Paragraph 6, Bold Underlined",
			"Paragraph 7, Italic Underlined",
			"Paragraph 8, Bold Italic Underlined",
			"Paragraph 9, ",
			"mixed Formats",
			"Tab\tTab",
			"HardLineBreak\nHardLineBreak",
			"ColumnBreak\016ColumnBreak",
			"NonBreakingHyphen\036NonBreakingHyphen", 
			"NonRequiredHyphen\037NonRequiredHyphen", 
			"NonBreakingSpace", 
			"\240", 
			"NonBreakingSpace"
		]
		assert_equal(expected, handler.texts)
	end
	def test_illegal_replacement_handler
		parser = Rwv2.create_parser(@filename)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		parser.set_inline_replacement_handler(@ir_handler)
		@ir_handler.non_required_hyphen = ""
		assert_raises(RuntimeError) {
			parser.parse
		}
		@ir_handler.non_required_hyphen = "--"
		assert_raises(RuntimeError) {
			parser.parse
		}
	end
	def test_subdocument_handler
		parser = Rwv2.create_parser(@filename4)
		handler = StubSubDocumentHandler.new
		assert_nothing_raised {
			parser.set_subdocument_handler(handler)
		}
		parser.parse
		assert_equal(1, handler.body_starts)
		assert_equal(1, handler.body_ends)
		assert_equal(1, handler.footnote_starts)
		assert_equal(1, handler.footnote_ends)
		assert_equal(1, handler.headers_starts)
		assert_equal(1, handler.headers_ends)
		assert_equal(2, handler.header_ends)
		assert_equal([Rwv2::HEADER_ODD, Rwv2::FOOTER_ODD], handler.header_starts)
	end
	def test_table_handler
		parser = Rwv2.create_parser(@filename5)
		handler = StubTableHandler.new
		assert_nothing_raised {
			parser.set_table_handler(handler)
		}
		parser.parse
		assert_equal(6, handler.row_ends)
		assert_equal(11, handler.cell_starts)
		assert_equal(11, handler.cell_ends)
		head = handler.row_starts.at(0)
		row0 = handler.row_starts.at(1)
		row1 = handler.row_starts.at(2)
		row2 = handler.row_starts.at(3)
		row3 = handler.row_starts.at(4)
		assert_equal(283, row0.row_height)
		assert_equal(-283, row1.row_height)
		assert_equal(2, row0.row_cells)
		assert_equal(2, row1.row_cells)
		assert_equal(3, row0.cell_boundaries.size)
		assert_equal(0, row0.cell_boundaries.at(0))
		assert_equal(4818, row0.cell_boundaries.at(1))
		assert_equal(9639, row0.cell_boundaries.at(2))
		assert_equal(2, row0.cell_descriptors.size)
		ct0 = row0.cell_descriptors.first
		cta = row0.cell_descriptors.last
		ctb = row1.cell_descriptors.first
		assert_instance_of(Rwv2::TableProperties::CellDescriptor, ct0)
		assert_equal(false, ct0.first_merged?)
		assert_equal(Rwv2::TableProperties::CellDescriptor::ALIGN_TOP, ct0.vertical_align)
		assert_equal(Rwv2::TableProperties::CellDescriptor::ALIGN_CENTER, cta.vertical_align)
		assert_equal(Rwv2::TableProperties::CellDescriptor::ALIGN_BOTTOM, ctb.vertical_align)
		ct2 = row3.cell_descriptors.first
		assert_equal(true, ct2.vertical_merged?)
		assert_equal(true, ct2.vertical_restart?)
		# ct2 = row3.cell_descriptors.first
		# assert_equal(true, ct2.vertical)

		# FIXME: the following are untested, 
		#	       need a _real_ Wordfile to test...
		# assert_equal(Rwv2::TableProperties::ALIGN_LEFT, row0.align)
		# assert_equal(Rwv2::TableProperties::ALIGN_LEFT, row1.align)
		# row0.gap_half
		# assert_equal(true, row0.cant_split)
		# assert_equal(false, row1.cant_split)
		# ct1 = row2.cell_descriptors.first
		# assert_equal(true, ct1.merged)
		# assert_equal(true, ct1.first_merged)
		# :rotate_font, :backward, :vertical_merged,
		# :vertical_restart, :vertical_align
	end
	def test_text_handler
		parser = Rwv2.create_parser(@filename)
		handler = StubTextHandler.new
		assert_nothing_raised {
			parser.set_text_handler(handler)
		}
		parser.parse
		expected = [
			"Paragraph 1, Standard",
			"Paragraph 2, Bold",
			"Paragraph 3, Italic",
			"Paragraph 4, Underlined",
			"Paragraph 5, Bold Italic",
			"Paragraph 6, Bold Underlined",
			"Paragraph 7, Italic Underlined",
			"Paragraph 8, Bold Italic Underlined",
			"Paragraph 9, ",
			"mixed Formats",
			"Tab\tTab",
			"HardLineBreak\vHardLineBreak",
			"ColumnBreak\016ColumnBreak",
			"NonBreakingHyphen\036NonBreakingHyphen", 
			"NonRequiredHyphen\037NonRequiredHyphen", 
			"NonBreakingSpace", 
			"\240", 
			"NonBreakingSpace"
		]
		assert_equal(expected, handler.texts)
		paps = handler.paragraph_properties
		assert_equal(16, paps.size)
		assert_equal(16, handler.paragraph_ends)
		pap0 = paps.at(0)
		pap1 = paps.at(1)
		pap2 = paps.at(2)
		pap3 = paps.at(3)
		pap4 = paps.at(4)
		pap5 = paps.at(5)
		pap6 = paps.at(6)
		assert_instance_of(Rwv2::ParagraphProperties, pap0)
		assert_equal(Rwv2::ALIGN_LEFT, pap0.align)
		assert_equal(Rwv2::ALIGN_CENTER, pap1.align)
		assert_equal(Rwv2::ALIGN_RIGHT, pap2.align)
		assert_equal(Rwv2::ALIGN_JUSTIFY, pap3.align)
		assert_equal(true, pap0.keep?)
		assert_equal(false, pap0.keep_with_next?)
		assert_equal(false, pap0.widow_control?)
		assert_equal(false, pap1.keep?)
		assert_equal(true, pap1.keep_with_next?)
		assert_equal(false, pap1.widow_control?)
		assert_equal(false, pap2.keep?)
		assert_equal(false, pap2.keep_with_next?)
		assert_equal(true, pap2.widow_control?)
		assert_equal(false, pap3.keep?)
		assert_equal(false, pap3.keep_with_next?)
		assert_equal(false, pap3.widow_control?)
		# FIXME does this work with a real Wordfile?
		# assert_equal(false, pap0.page_break_before?)
		# assert_equal(false, pap1.page_break_before?)
		# assert_equal(false, pap2.page_break_before?)
		# assert_equal(true, pap3.page_break_before?)
		assert_equal(1680, pap4.indent_right)
		assert_equal(0, pap4.indent_left)
		assert_equal(0, pap4.indent_first_line)
		assert_equal(0, pap5.indent_right)
		assert_equal(570, pap5.indent_left)
		assert_equal(0, pap5.indent_first_line)
		assert_equal(0, pap6.indent_right)
		assert_equal(570, pap6.indent_left)
		assert_equal(-285, pap6.indent_first_line)
	end
	def test_incomplete_text_handler
		parser = Rwv2.create_parser(@filename)
		handler = Rwv2::TextHandler.new
		parser.set_text_handler(handler)
		assert_nothing_raised { parser.parse }
	end
	def test_character_properties
		parser = Rwv2.create_parser(@filename)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		parser.parse
		formats = handler.formats
		assert_equal(false, formats.empty?, 
			"The Parser recorded no Character Properties")
		bold = handler.formats.collect { |fmt| fmt.bold? } 
		expected = [1,4,5,7,9] 
		assert_equal(expected, index_select(formats, :bold?))
		expected = [2,4,6,7]
		assert_equal(expected, index_select(formats, :italic?))
	end
	def test_character_properties2
		parser = Rwv2.create_parser(@filename2)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		parser.parse
		expected = [ 
			"The ", "new Text!", 
			"This will be deleted.", 
			"Outlined", 
			"Small Caps", 
			"Caps", 
			"Strikethrough", 
			"Shadow", 
			"Lower Case", 
			"Embossed", 
			"Engraved", 
			"Double Strikethrough",
		]
		assert_equal(expected, handler.texts)
		formats = handler.formats
		assert_equal(false, formats.empty?, 
			"The Parser recorded no Character Properties")
		assert_equal([0,1], index_select(formats, :rev_mark?))
		assert_equal([2], index_select(formats, :rev_mark_del?))
		assert_equal([3], index_select(formats, :outline?))
		assert_equal([4], index_select(formats, :small_caps?))
		assert_equal([5], index_select(formats, :caps?))
		assert_equal([6], index_select(formats, :strikethrough?))
		assert_equal([7], index_select(formats, :shadow?))
		#assert_equal([8], index_select(formats, :lowercase?)) # FIXME 
		# our test-file is made with Openoffice - which does not set the
		# lowercase-flag in Word Files...
		assert_equal([9], index_select(formats, :emboss?))
		assert_equal([10], index_select(formats, :imprint?))
		assert_equal([11], index_select(formats, :double_strikethrough?))
	end
	def test_character_properties3
		parser = Rwv2.create_parser(@filename3)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		parser.parse
		expected = [ 
			"Normal", 
			"Superscript",
			"Subscript",
			"Single",
			"By Word",
			"Double",
			"Dotted",
			"Thick",
			"Dash",
			"Dot Dash",
			"Dot Dot Dash",
			"Wave",
		]
		assert_equal(expected, handler.texts)
		formats = handler.formats
		assert_equal(false, formats.empty?, 
			"The Parser recorded no Character Properties")
		assert_equal(Rwv2::CharacterProperties::POSITION_NORMAL, formats[0].position)
		assert_equal(Rwv2::CharacterProperties::POSITION_SUPERSCRIPT, formats[1].position)
		assert_equal(Rwv2::CharacterProperties::POSITION_SUBSCRIPT, formats[2].position)
		assert_equal(false, formats[0].underline)
		#assert_equal([3,4,5,6,7,8,9,10,11], index_select(formats, :underline)) # FIXME
		# Openoffice saves simple underline somewhere else?
		assert_equal([4,5,6,7,8,9,10,11], index_select(formats, :underline)) # FIXME
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_NONE, formats[0].underline)
		#assert_equal(Rwv2::CharacterProperties::UNDERLINE_SINGLE, formats[3].underline) # FIXME
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_BY_WORD, formats[4].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_DOUBLE, formats[5].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_DOTTED, formats[6].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_THICK, formats[7].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_DASH, formats[8].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_DOT_DASH, formats[9].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_DOT_DOT_DASH, formats[10].underline) 
		assert_equal(Rwv2::CharacterProperties::UNDERLINE_WAVE, formats[11].underline) 
	end
	def test_section_properties
		parser = Rwv2.create_parser(@filename6)
		handler = StubTextHandler.new
		parser.set_text_handler(handler)
		parser.parse
		properties = handler.section_properties
		assert_equal(5, properties.size)
		assert_equal(5, handler.section_ends)
		assert_equal(1, handler.page_breaks)
		sect0 = properties.at(0)
		sect1 = properties.at(1)
		sect2 = properties.at(2)
		sect3 = properties.at(3)
		sect4 = properties.at(4)
		assert_instance_of(Rwv2::SectionProperties, sect0)
		assert_equal(Rwv2::SectionProperties::NUMBER_ARABIC, sect0.page_number_format)
		assert_equal(false, sect0.title_page?)
		assert_equal(false, sect0.unlocked?)
		assert_equal(false, sect0.page_number_restart?)
		assert_equal(false, sect0.line_numbering_modulus)
		assert_equal(1, sect0.columns)
		assert_equal(2, sect1.columns)
		assert_equal(3, sect2.columns)
		assert_equal(2, sect3.columns)
		assert_equal(1, sect4.columns)

		# FIXME: get a real wordfile...
		# assert_equal(Rwv2::SectionProperties::BREAK_NONE, sect0.break_code)
		# assert_equal(Rwv2::SectionProperties::BREAK_NONE, sect1.break_code)
		# assert_equal(Rwv2::SectionProperties::BREAK_NONE, sect2.break_code)
		# assert_equal(Rwv2::SectionProperties::BREAK_NONE, sect3.break_code)
		# assert_equal(Rwv2::SectionProperties::BREAK_NONE, sect4.break_code)
		# assert_equal(Rwv2::SectionProperties::LINE_NUMBERING_PER_PAGE, sect0.line_numbering_code)
		# assert_equal(true, sect0.endnote?)
		# assert_equal(true, sect1.endnote?)
		# assert_equal(false, sect2.endnote?)
		# assert_equal(true, sect3.endnote?)
	end
	def test_gc
		parser = Rwv2.create_parser(@filename)
		parser.set_inline_replacement_handler(@ir_handler.dup)
		parser.set_subdocument_handler(StubSubDocumentHandler.new)
		parser.set_text_handler(StubTextHandler.new)
		GC.start
		sleep(0.5)
		assert_nothing_raised {
			parser.parse
		}
	end
	# helper methods
	def index_select(collection, symbol)
		res = []
		collection.each_with_index { |item, idx|
			(res << idx) if(item.send(symbol))
		}
		res
	end
end
