require 'test/unit'
require 'lib/metric_listeners'
require 'lib/metrics_collector'
require 'rubygems'
require 'mocha'

class TestListenerAdapter < Test::Unit::TestCase
  def setup
    collector = MetricsCollector.new
    listeners = {"tag" => :FullListener}
    @metrics_listener = ListenerAdapter.new("Abc", collector, listeners)
  end
  
  def test_should_not_initialize_strategy_on_invalid_tag
    @metrics_listener.tag_end("tagg")
    assert_nil @metrics_listener.instance_variable_get(:@strategy)
  end
  
  def test_should_initialize_strategy_on_valid_tag
    @metrics_listener.tag_end("tag")
    assert @metrics_listener.instance_variable_get(:@strategy).is_a?(FullListener)
  end
  
  def test_should_process_end_tag_with_valid_strategy
    @metrics_listener.tag_end("tag")
    delegate = @metrics_listener.instance_variable_get(:@strategy)
    delegate.expects(:tag_end).with("tag2")
    @metrics_listener.tag_end("tag2")
  end

  def test_should_process_start_tag_with_valid_strategy
    @metrics_listener.tag_end("tag")
    delegate = @metrics_listener.instance_variable_get(:@strategy)
    delegate.expects(:tag_start).with("tag2", [1, 2])
    @metrics_listener.tag_start("tag2", [1, 2])
  end

  def test_should_not_break_when_processing_start_tag_without_strategy
    delegate = @metrics_listener.instance_variable_get(:@strategy)
    delegate.stubs(:tag_start).raises(Exception)
    @metrics_listener.tag_start("tag2", [1, 2, 3])
  end
end

class FullListenerTest < Test::Unit::TestCase
  def setup
    @collector = MetricsCollector.new
    @listener = FullListener.new("Abc", @collector)
  end
  
  def test_should_store_type
    @listener.tag_start("Type", {"handle" => "ClassFoo"})
    assert_equal "ClassFoo", @listener.instance_variable_get(:@inType)
    @listener.tag_end("Type")
    assert_nil @listener.instance_variable_get(:@inType)
  end

  def test_should_store_package
    @listener.tag_start("Package", {"handle" => "br.org.agilcoop"})
    assert_equal "br.org.agilcoop", @listener.instance_variable_get(:@inPackage)
    @listener.tag_end("Package")
    assert_nil @listener.instance_variable_get(:@inPackage)
  end
  
  def test_should_know_if_in_method
    @listener.tag_start("Method", {})
    assert @listener.instance_variable_get(:@inMethod)
    @listener.tag_end("Method")
    assert !@listener.instance_variable_get(:@inMethod)
  end
  
  def test_should_collect_class_metric
    @collector.expects(:new_metric).with("ClassFoo", 6.54)
    
    @listener.tag_start("Type", {"handle" => "ClassFoo"})
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "6.54"})
  end

  def test_should_collect_class_metric_with_float_translated
    @collector.expects(:new_metric).with("ClassFoo", 6.54)
    
    @listener.tag_start("Type", {"handle" => "ClassFoo"})
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "6,54"})
  end

  def test_should_not_collect_class_metric_with_wrong_id
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Type", {"handle" => "ClassFoo"})
    @listener.tag_start("Metric", {"id" =>"WRONG", "value" => "6.54"})
  end

  def test_should_not_collect_class_metric_inside_method
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Type", {"handle" => "ClassFoo"})
    @listener.tag_start("Method", {})
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "6.54"})
  end
  
  def test_should_not_collect_class_metric_without_type
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "6.54"})
  end

  def test_should_collect_package_metric
    @collector.expects(:new_metric).with("br.org.agilcoop", 8.54)
    
    @listener.tag_start("Package", {"handle" => "br.org.agilcoop"})
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "8.54"})
  end

  def test_should_collect_package_metric_with_float_translated
    @collector.expects(:new_metric).with("br.org.agilcoop", 8.54)
    
    @listener.tag_start("Package", {"handle" => "br.org.agilcoop"})
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "8,54"})
  end

  def test_should_not_collect_package_metric_outside_package
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "8.54"})
  end

  def test_should_not_collect_package_metric_when_already_aggregated
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Package", {"handle" => "br.org.agilcoop"})
    @listener.tag_start("Metric", {"id" =>"Abc", "value" => "8.54", "per" => "type"})
  end
end

class FlatListenerTest < Test::Unit::TestCase
  def setup
    @collector = MetricsCollector.new
    @listener = FlatListener.new("Abc", @collector)
  end
  
  def test_should_recognize_metric
    @listener.tag_start("Metric", {"id" =>"Abc"})
    assert @listener.instance_variable_get(:@inMetric)
    @listener.tag_end("Metric")
    assert !@listener.instance_variable_get(:@inMetric)
  end

  def test_should_not_recognize_metric_with_wrong_id
    @listener.tag_start("Metric", {"id" =>"WRONG"})
    assert !@listener.instance_variable_get(:@inMetric)
  end

  def test_should_recognize_values_per_method_inside_metric
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Values", {"per" => "method"})
    assert @listener.instance_variable_get(:@perMethod)
    @listener.tag_end("Values")
    assert !@listener.instance_variable_get(:@perMethod)
  end

  def test_should_not_recognize_values_per_method_outside_metric
    @listener.tag_start("Values", {"per" => "method"})
    assert !@listener.instance_variable_get(:@perMethod)
  end

  def test_should_recognize_values_per_package_inside_metric
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Values", {"per" => "packageFragment"})
    assert @listener.instance_variable_get(:@perPackage)
    @listener.tag_end("Values")
    assert !@listener.instance_variable_get(:@perPackage)
  end

  def test_should_not_recognize_values_per_package_outside_metric
    @listener.tag_start("Values", {"per" => "packageFragment"})
    assert !@listener.instance_variable_get(:@perPackage)
  end
  
  def test_should_aggregate_value_per_class
    @collector.expects(:new_metric).with("br.org.agilcoop.ClassFoo", 1.2)
    
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1.2"})
  end

  def test_should_aggregate_translated_value_per_class
    @collector.expects(:new_metric).with("br.org.agilcoop.ClassFoo", 1.2)
    
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1,2"})
  end

  def test_should_add_metric_value_per_class
    @collector.expects(:new_metric).with("br.org.agilcoop.ClassFoo", 1.2)
    @collector.expects(:add_metric).with("br.org.agilcoop.ClassFoo", 1.3)
    
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1,2"})
    @listener.tag_start("Values", {"per" => "method"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1,3"})
  end

  def test_should_aggregate_value_per_package
    @collector.expects(:new_metric).with("br.org.agilcoop", 1.5)
    
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Values", {"per" => "packageFragment"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "value" => "1.5"})
  end

  def test_should_aggregate_translated_value_per_package
    @collector.expects(:new_metric).with("br.org.agilcoop", 1.6)
    
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Values", {"per" => "packageFragment"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "value" => "1,6"})
  end

  def test_not_should_aggregate_value_per_class_outside_metric
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1.2"})
  end

  def test_should_not_add_metric_value_per_class_outside_metric
    @collector.expects(:new_metric).with("br.org.agilcoop.ClassFoo", 1.2)
    @collector.stubs(:add_metric).raises(Exception)
    
    @listener.tag_start("Metric", {"id" =>"Abc"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1,2"})
    @listener.tag_start("Values", {"per" => "method"})
    @listener.tag_end("Metric")
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "source" => "ClassFoo", "value" => "1,3"})
  end

  def test_should_not_aggregate_value_per_package_outside_metric
    @collector.stubs(:new_metric).raises(Exception)
    
    @listener.tag_start("Values", {"per" => "packageFragment"})
    @listener.tag_start("Value", {"package" => "br.org.agilcoop", "value" => "1.5"})
  end
end