require 'test/unit'
require 'external/test_support'
IWATestSupport.set_src_dir
require 'iowa/dispatchers/StandardDispatcher'
require 'benchmark'

class ExternalRoutines
	def self.downcase(r)
		r.uri.downcase!
	end
end

class FauxPolicy
	def self.getIDs(_)
		[nil,nil]
	end
end

module Iowa
	def self.app
		Struct.new(:policy).new(FauxPolicy)
	end
end
		
class TC_StandardDispatcher < Test::Unit::TestCase

	@@testdir = IWATestSupport.test_dir(__FILE__)
	class Request
		attr_accessor :uri, :hostname, :params, :request_method
		def initialize(uri = nil, hostname = nil, params = nil, request_method = 'GET')
			@uri = uri
			@hostname = hostname
			@params = params
			@request_method = request_method
		end
	end

	def transforming(uri)
		print "\nTransforming #{uri} -> "
	end

	def processing(req)
		puts "\nProcessing request #{req.inspect} -> "
	end

	def setup
		Dir.chdir(@@testdir)
		IWATestSupport.announce(:standard_dispatcher,"Iowa::Dispatchers::StandardDisparcher")
	end

	def test_a_empty_struct
		rule_struct = []
		assert_nothing_raised("Failure parsing an empty ruleset") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
		end
	end

	def test_b_hash_struct1
		rule_struct = {:match => /view/, :sub => 'show'}
		rq = Request.new('/view/3')
		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming rq.uri
			rs.process(rq)
			puts rq.uri
			assert_equal('/show/3',rq.uri,"The processed request did not return the expected result.")
		end
	end

	def test_c_hash_struct2
		rule_struct = {:match => 'view', :sub => 'show'}
		rq = Request.new('/view/3')
		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming rq.uri
			rs.process(rq)
			puts rq.uri
			assert_equal('/show/3',rq.uri,"The processed request did not return the expected result.")
		end
	end

	def test_d_simple_struct
		rule_struct = [
			{:match => '[A-Z]', :gsub => '{|r,c| c.downcase}'},
			{:match => '\.htm$', :sub => '.html'},
			{:match => '^.*\.php\d*', :sub => '/php_unsupported.html'},
		]
		rqs = [
			Request.new('/foo/bar.htm'),
			Request.new('/app.php'),
			Request.new('/foo/BAR.HTM')
		]
		rqso = rqs.dup

		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming rqso[0].uri
			rs.process(rqs[0])
			puts rqs[0].uri
			assert_equal('/foo/bar.html',rqs[0].uri,"Transformation incorrect.")

			transforming rqso[1].uri
			rs.process(rqs[1])
			puts rqs[1].uri
			assert_equal('/php_unsupported.html',rqs[1].uri,"Transformation incorrect.")

			transforming rqso[2].uri
			rs.process(rqs[2])
			puts rqs[2].uri
			assert_equal('/foo/bar.html',rqs[2].uri,"Transformation incorrect.")
		end
	end

	def test_e_simple_struct_call_external1
		rule_struct = [
			{:match => '[A-Z]', :call => '::ExternalRoutines.downcase'},
			{:match => '\.htm$', :sub => '.html'}
		]
		rqs = [
			Request.new('/foo2/bar.htm'),
			Request.new('/foo2/BAR.HTM')
		]
		rqso = rqs.dup

		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming rqso[0].uri
			rs.process(rqs[0])
			puts rqs[0].uri
			assert_equal('/foo2/bar.html',rqs[0].uri,"Transformation incorrect.")

			transforming rqso[1].uri
			rs.process(rqs[1])
			puts rqs[1].uri
			assert_equal('/foo2/bar.html',rqs[1].uri,"Transformation incorrect.")
		end
	end

	def test_f_simple_struct_call_external2
		rule_struct = [
			{:match => '[A-Z]', :call => 'external_downcase'},
			{:match => '\.htm$', :sub => '.html'}
		]
		rqs = [
			Request.new('/foo3/bar.htm'),
			Request.new('/foo3/BAR.HTM')
		]
		rqso = rqs.dup

		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming rqso[0].uri
			rs.process(rqs[0])
			puts rqs[0].uri
			assert_equal('/foo3/bar.html',rqs[0].uri,"Transformation incorrect.")

			transforming rqso[1].uri
			rs.process(rqs[1])
			puts rqs[1].uri
			assert_equal('/foo3/bar.html',rqs[1].uri,"Transformation incorrect.")
		end
	end

	def test_g_simple_struct_call_external3
		rule_struct = [
			{:match => '[A-Z]', :eval => 'request.uri.downcase!'},
			{:match => '\.htm$', :sub => '.html'}
		]
		rqs = [
			Request.new('/foo4/bar.htm'),
			Request.new('/foo4/BAR.HTM')
		]
		rqso = rqs.dup

		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming rqso[0].uri
			rs.process(rqs[0])
			puts rqs[0].uri
			assert_equal('/foo4/bar.html',rqs[0].uri,"Transformation incorrect.")

			transforming rqso[1].uri
			rs.process(rqs[1])
			puts rqs[1].uri
			assert_equal('/foo4/bar.html',rqs[1].uri,"Transformation incorrect.")
		end
	end

	def test_h_branching
		rule_struct = [
			{:match => '{|request| request.hostname =~ /\.?foo\.com/}',
				:branch => [{:match => '(?i-mx:\.htm)$', :sub => '.html'}]},
			{:match => '{|request| request.hostname =~ /\.?bar\.com/}',
				:branch => [{:match => '[A-Z]', :eval => 'request.uri.downcase!'}]}
		]
		rqs = [
			Request.new('/INDEX.HTM','foo.com'),
			Request.new('/INDEX.HTM','www.foo.com'),
			Request.new('/INDEX.HTM','www.bar.com'),
			Request.new('/INDEX.HTM','fluffy.poodlelovers.com')
		]
		rqso = rqs.dup

		assert_nothing_raised("Failure while parsing ruleset.") do
			rs = Iowa::Dispatchers::StandardDispatcher::RuleSet.new(rule_struct)
			transforming "#{rqso[0].uri} @ #{rqso[0].hostname}"
			rs.process(rqs[0])
			puts rqs[0].uri
			assert_equal('/INDEX.html',rqs[0].uri,"Transformation incorrect.")

			transforming "#{rqso[1].uri} @ #{rqso[1].hostname}"
			rs.process(rqs[1])
			puts rqs[1].uri
			assert_equal('/INDEX.html',rqs[1].uri,"Transformation incorrect.")

			transforming "#{rqso[2].uri} @ #{rqso[2].hostname}"
			rs.process(rqs[2])
			puts rqs[2].uri
			assert_equal('/index.htm',rqs[2].uri,"Transformation incorrect.")

			transforming "#{rqso[3].uri} @ #{rqso[3].hostname}"
			rs.process(rqs[3])
			puts rqs[3].uri
			assert_equal('/INDEX.HTM',rqs[3].uri,"Transformation incorrect.")
		end
	end

	def test_i_processing
		mapfile = <<EMAPFILE
:rewrites:
- :match: "{|request| request.hostname =~ /\.?foo\.com/}"
  :branch: 
  - :match: "(?i-mx:\.htm)$"
    :sub: ".html"
- :match: "{|request| request.hostname =~ /\.?bar\.com/}"
  :branch:
  - :match: "[A-Z]"
    :eval: "request.uri.downcase!"
:map:
  /INDEX.html: CapIndex
  /index.htm: Index
  /INDEX.HTM: IndexHTM
  /randompix.png: RandomPix
EMAPFILE
		rqs = [
			Request.new('/INDEX.HTM','foo.com',{}),
			Request.new('/INDEX.HTM','www.foo.com',{}),
			Request.new('/INDEX.HTM','www.bar.com',{}),
			Request.new('/INDEX.HTM','fluffy.poodlelovers.com',{}),
			Request.new('/randompix.png','',{}),
			Request.new('/books',nil,{}),
			Request.new('/books/1',nil,{}),
			Request.new('/books/1',nil,{},'DELETE'),
			Request.new('/books/1',nil,{},'PUT'),
			Request.new('/books/new',nil,{}),
			Request.new('/books',nil,{},'POST'),
			Request.new('/books/1',nil,{'_method' => 'DELETE'},'POST'),
			Request.new('/books/1',nil,{'_method' => 'PUT'},'POST'),
			Request.new('/books/1;complete',nil,{},'POST'),
			Request.new('/books/show/ab4814f904/5/new',nil,{}),
			Request.new('/books/ab4814f904/5/new;show',nil,{})
		]
		rqso = rqs.dup

		require 'stringio'
		require 'yaml'
		mf = YAML.load(StringIO.new(mapfile))
		puts mf.inspect
		#assert_nothing_raised("Failure while parsing ruleset.") do
			disp = Iowa::Dispatchers::StandardDispatcher.new(mf)
			dd = disp.process(rqs[0])
			processing rqso[0]
			puts "**  #{dd.inspect}"
			assert(dd.component == 'CapIndex',"CapIndex1: Processing with result from map failed.")

			dd = disp.process(rqs[1])
			processing rqso[1]
			puts "  #{dd.inspect}"
			assert(dd.component == 'CapIndex',"CapIndex2: Processing with result from map failed.")

			dd = disp.process(rqs[2])
			processing rqso[2]
			puts "  #{dd.inspect}"
			assert(dd.component == 'Index',"Index: Processing with result from map failed.")

			dd = disp.process(rqs[3])
			processing rqso[3]
			puts "  #{dd.inspect}"
			assert(dd.component == 'IndexHTM',"IndexHTM: Processing with result from map failed.")

			dd = disp.process(rqs[4])
			processing rqso[4]
			puts "  #{dd.inspect}"
			assert(dd.component == 'RandomPix',"RandomPix: Processing with result from map failed.")

			dd = disp.process(rqs[5])
			processing rqso[5]
			puts "  #{dd.inspect}"
			assert(dd.component == 'books',"Processing RESTful request failed.")

			dd = disp.process(rqs[6])
			processing rqso[6]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'show') and (dd.args == ["1"])),"Processing RESTful request failed.")

			dd = disp.process(rqs[7])
			processing rqso[7]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'destroy') and (dd.args == ["1"])),"Processing RESTful DELETE request failed.")

			dd = disp.process(rqs[8])
			processing rqso[8]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'update') and (dd.args == ["1"])),"Processing RESTful PUT request failed.")

			dd = disp.process(rqs[9])
			processing rqso[9]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'new')),"Processing RESTful GET to invoke 'new' request failed.")

			dd = disp.process(rqs[10])
			processing rqso[10]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'create')),"Processing RESTful POST to invoke 'create' request failed.")

			dd = disp.process(rqs[11])
			processing rqso[11]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'destroy') and (dd.args == ["1"])),"Processing RESTful POST to invoke DELETE request failed.")

			dd = disp.process(rqs[12])
			processing rqso[12]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'update') and (dd.args == ["1"])),"Processing RESTful POST to invoke PUT request failed.")

			dd = disp.process(rqs[13])
			processing rqso[13]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'complete') and (dd.args == ["1"])),"Processing RESTful POST to invoke 'complete' request failed.")

			dd = disp.process(rqs[14])
			processing rqso[14]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'show') and (dd.args == ["ab4814f904","5","new"])),"Processing RESTful POST to invoke 'complete' request failed.")

			dd = disp.process(rqs[15])
			processing rqso[15]
			puts "  #{dd.inspect}"
			assert(((dd.component == 'books') and (dd.method == 'show') and (dd.args == ["ab4814f904","5","new"])),"Processing RESTful POST to invoke 'complete' request failed.")

			rqs = rqso.dup
			rqs.each {|rq| assert(disp.handleRequest?(rq),"handleRequest? says it won't handle #{rq.inspect}")}

			puts "\nDoing a quick benchmark of request processing using all of the test cases; 20000 * #{rqs.length} == #{20000 * rqs.length} requests."
			Benchmark.benchmark {|bm| bm.report {20000.times {rqs.each {|rq| dd = disp.process(rq)}}}}
		#end
	end
end

def external_downcase(r)
	r.uri.downcase!
end