diff --git a/spec/lib/report_formater/c3_formatter_spec.rb b/spec/lib/report_formater/c3_formatter_spec.rb
new file mode 100644
index 00000000000..cc56c407bae
--- /dev/null
+++ b/spec/lib/report_formater/c3_formatter_spec.rb
@@ -0,0 +1,95 @@
+describe ReportFormatter::C3Formatter do
+ include Spec::Support::ReportHelper
+
+ before(:each) do
+ allow(Charting).to receive(:backend).and_return(:c3)
+ allow(Charting).to receive(:format).and_return(:c3)
+ end
+
+ describe "#add_series" do
+ it "does not raise error for 'stack' chart" do
+ report = numeric_chart_3d(true)
+ expect { render_report(report) }.to_not raise_error
+ end
+ end
+
+ context '#build_numeric_chart_grouped' do
+ [true, false].each do |other|
+ it "builds 2d numeric charts from summaries #{other ? 'with' : 'without'} 'other'" do
+ report = numeric_charts_2d_from_summaries(other)
+
+ expect_any_instance_of(described_class).to receive(:build_numeric_chart_grouped).once.and_call_original
+ render_report(report)
+ expect(report.chart[:data][:columns][0][1]).to eq(4.0)
+ expect(report.chart[:data][:columns][0][-1]).to eq(4) if other
+ end
+ end
+ end
+
+ context '#build_numeric_chart_simple' do
+ let(:report) { numeric_chart_simple }
+ let(:long_report) { numeric_chart_simple_with_long_strings }
+
+ it "report chart have right data in ascending order" do
+ report.col_formats = [nil, :general_number_precision_0]
+ render_report(report)
+ expect(report.chart[:data][:columns][0].count).to eq(report.table.data.count + 1)
+ expect(report.chart[:data][:columns][0][1]).to eq(2024)
+ end
+
+ it "handles null data in chart column" do
+ report = null_data_chart
+
+ expect_any_instance_of(described_class).to receive(:build_numeric_chart_simple).once.and_call_original
+ render_report(report)
+ end
+
+ it "handle long strings" do
+ render_report(long_report)
+ expect(long_report.chart[:miq][:category_table][2]).to eq(long_category)
+ expect(long_report.chart[:miq][:name_table]['1']).to eq('RAM Size (MB)')
+ end
+ end
+
+ context '#build_numeric_chart_simple' do
+ [true, false].each do |other|
+ it "builds 2d numeric charts #{other ? 'with' : 'without'} 'other'" do
+ report = numeric_chart_simple2(other)
+
+ expect_any_instance_of(described_class).to receive(:build_numeric_chart_simple).once.and_call_original
+ render_report(report)
+ expect(report.chart[:data][:columns][0][1]).to eq(15)
+ expect(report.chart[:data][:columns][0][-1]).to eq(1) if other
+ end
+ end
+
+ it "handles null data in chart column" do
+ report = null_data_chart
+
+ expect_any_instance_of(described_class).to receive(:build_numeric_chart_simple).once.and_call_original
+ render_report(report)
+ end
+ end
+
+ context '#build_numeric_chart_grouped_2dim' do
+ [true, false].each do |other|
+ it "builds 3d numeric charts #{other ? 'with' : 'without'} 'other'" do
+ report = numeric_chart_3d(other)
+
+ expect_any_instance_of(described_class).to receive(:build_numeric_chart_grouped_2dim).once.and_call_original
+ render_report(report)
+ expect(report.chart[:data][:columns][0][1]).to eq(6_656)
+ expect(report.chart[:data][:columns][0][2]).to eq(4_096)
+ expect(report.chart[:data][:columns][1][1]).to eq(1_024)
+ expect(report.chart[:data][:columns][0][-1]).to eq(1_024) if other
+ end
+ end
+
+ it 'handles namespace-prefixed class names in chart column' do
+ report = chart_with_namespace_prefix
+
+ expect_any_instance_of(described_class).to receive(:build_numeric_chart_grouped_2dim).once.and_call_original
+ render_report(report)
+ end
+ end
+end
diff --git a/spec/lib/report_formater/chart_common_spec.rb b/spec/lib/report_formater/chart_common_spec.rb
new file mode 100644
index 00000000000..748332855c6
--- /dev/null
+++ b/spec/lib/report_formater/chart_common_spec.rb
@@ -0,0 +1,54 @@
+# describe ReportFormatter::ChartCommon do
+#
+# We have to operate on the specific class although we are testing the common behavior.
+# Otherwise expect_any_instance_of(described_class).to receive(:build_performance_chart_area).once.and_call_original
+# leads to with:
+# SystemStackError:
+# stack level too deep
+# # ./lib/report_formatter/chart_common.rb:555:in `build_performance_chart'
+# # ./lib/report_formatter/chart_common.rb:57:in `call'
+
+describe ReportFormatter::C3Formatter do
+ include Spec::Support::ReportHelper
+
+ before(:each) do
+ allow(Charting).to receive(:backend).and_return(:c3)
+ allow(Charting).to receive(:format).and_return(:c3)
+ end
+ context '#build_performance_chart_area' do
+ it "builds a daily chart with all nils" do
+ report = MiqReport.new(
+ :db => "VimPerformanceDaily",
+ :cols => cols = %w(timestamp cpu_usagemhz_rate_average min_cpu_usagemhz_rate_average max_cpu_usagemhz_rate_average trend_max_cpu_usagemhz_rate_average resource.cpu_usagemhz_rate_average_high_over_time_period resource.cpu_usagemhz_rate_average_low_over_time_period),
+ :include => {
+ "resource" => {
+ "columns" => %w(cpu_usagemhz_rate_average_high_over_time_period cpu_usagemhz_rate_average_low_over_time_period derived_memory_used_high_over_time_period derived_memory_used_low_over_time_period),
+ }
+ },
+ :col_order => cols,
+ :headers => ["Date/Time", "Avg Used", "Max Available", "Max Reserved", "Trend Max Used", "foo", "bar"],
+ :order => "ascending",
+ :sortby => "timestamp",
+ :group => "n",
+ :graph => {
+ :type => "Line",
+ :columns => %w(cpu_usagemhz_rate_average min_cpu_usagemhz_rate_average max_cpu_usagemhz_rate_average trend_max_cpu_usagemhz_rate_average resource.cpu_usagemhz_rate_average_high_over_time_period resource.cpu_usagemhz_rate_average_low_over_time_period),
+ :legends => nil,
+ :max_col_size => nil
+ },
+ :dims => nil,
+ :col_formats => nil,
+ :col_options => nil,
+ :rpt_options => nil,
+ )
+
+ report.table = Ruport::Data::Table.new(
+ :column_names => %w(timestamp cpu_usagemhz_rate_average min_cpu_usagemhz_rate_average max_cpu_usagemhz_rate_average trend_max_cpu_usagemhz_rate_average),
+ :data => [["Sun, 20 Mar 2016 00:00:00 UTC +00:00", 0.0, nil, nil, 0]])
+
+ expect_any_instance_of(described_class).to receive(:build_performance_chart_area).once.and_call_original
+ render_report(report) { |e| e.options.graph_options[:chart_type] = :performance }
+ expect(report.chart[:data]).to be
+ end
+ end
+end
diff --git a/spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb b/spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb
new file mode 100644
index 00000000000..74a9b588da4
--- /dev/null
+++ b/spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb
@@ -0,0 +1,77 @@
+describe ReportFormatter::JqplotFormatter do
+ include Spec::Support::ReportHelper
+
+ before(:each) do
+ allow(Charting).to receive(:backend).and_return(:jqplot)
+ allow(Charting).to receive(:format).and_return(:jqplot)
+ end
+ context '#build_reporting_chart_dim2' do
+ it 'builds a stacked chart' do
+ report = MiqReport.new(
+ :db => "Vm",
+ :cols => %w(os_image_name),
+ :include => {"ext_management_system" => {"columns" => ["name"]}},
+ :col_order => ["ext_management_system.name", "os_image_name"],
+ :headers => ["Cloud/Infrastructure Provider Name", "OS Name"],
+ :order => "Ascending",
+ :group => nil,
+ :graph => {:type => "StackedBar", :mode => "counts", :column => nil, :count => 10, :other => false},
+ :dims => 2,
+ :col_options => {},
+ :rpt_options => {},
+ :sortby => %w(ext_management_system.name os_image_name)
+ )
+
+ report.table = Ruport::Data::Table.new(
+ :column_names => %w(os_image_name ext_management_system.name id),
+ :data => [
+ %w(linux_centos MTC-RHEVM-3.0 10000000000012),
+ %w(linux_centos MTC-RHEVM-3.0 10000000000013),
+ %w(linux_redhat MTC-RHEVM-3.1 10000000000014),
+ %w(linux_centos MTC-RHEVM-3.1 10000000000015),
+ ]
+ )
+
+ expect_any_instance_of(described_class).to receive(:build_reporting_chart_dim2).once.and_call_original
+ render_report(report)
+ expect(report.chart[:data]).to eq([[2, 1], [0, 1]])
+ expect(report.chart[:options][:seriesDefaults][:renderer]).to eq("jQuery.jqplot.BarRenderer")
+ expect(report.chart[:options][:series]).to eq([{:label => "linux_centos"}, {:label => "linux_redhat"}])
+ end
+ end
+
+ context "#build_reporting_chart_other" do
+ it 'builds a pie chart' do
+ report = MiqReport.new(
+ :db => "Host",
+ :cols => %w(os_image_name),
+ :include => {},
+ :col_order => ["os_image_name"],
+ :headers => ["OS Name"],
+ :order => "Ascending",
+ :sortby => ["os_image_name"],
+ :group => nil,
+ :graph => {:type => "Pie", :mode => "counts", :column => nil, :count => 10, :other => true},
+ :dims => 1,
+ :col_options => {},
+ :rpt_options => {},
+ )
+
+ report.table = Ruport::Data::Table.new(
+ :column_names => %w(os_image_name id),
+ :data => [
+ ["linux_esx", 5],
+ ["linux_esx", 6],
+ ["linux_esx", 7],
+ ["widloze", 8],
+ ]
+ )
+
+ expect_any_instance_of(described_class).to receive(:build_reporting_chart_other).once.and_call_original
+ render_report(report)
+ expect(report.chart[:data][0]).to eq([["linux_esx: 3", 3], ["widloze: 1", 1]])
+ expect(report.chart[:options][:seriesDefaults][:renderer]).to eq("jQuery.jqplot.PieRenderer")
+ expect(report.chart[:options][:highlighter]).to be_truthy
+ end
+ end
+end
diff --git a/spec/lib/report_formater/timeline_spec.rb b/spec/lib/report_formater/timeline_spec.rb
new file mode 100644
index 00000000000..498b75608da
--- /dev/null
+++ b/spec/lib/report_formater/timeline_spec.rb
@@ -0,0 +1,193 @@
+describe ReportFormatter::ReportTimeline do
+ context '#bubble_icon' do
+ def stub_bottleneck_event(resource_type, ems_type = nil)
+ bottleneck_event = BottleneckEvent.create!(:resource_type => resource_type)
+ unless ems_type.nil?
+ ems = FactoryGirl.create(:ems_redhat)
+ allow(ems).to receive(:emstype).and_return(ems_type)
+ allow(bottleneck_event).to receive(:resource).and_return(ems)
+ end
+ bottleneck_event
+ end
+
+ it 'shows a generic icon for MiqEnterprise' do
+ expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(MiqEnterprise))).to eq('enterprise')
+ end
+
+ it 'shows a generic icon for EmsCluster' do
+ expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(EmsCluster))).to eq('cluster')
+ end
+
+ it 'shows a generic icon for ExtManagementSystem' do
+ expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(ExtManagementSystem))).to eq('ems')
+ end
+
+ it 'shows a Red Hat logo for RHEVM EMS' do
+ expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(ExtManagementSystem, 'rhevm'))).to eq('vendor-redhat')
+ end
+ end
+end
+
+describe ReportFormatter::TimelineMessage do
+ describe '#message_html on container event' do
+ row = {}
+ let(:ems) { FactoryGirl.create(:ems_redhat, :id => 42) }
+ let(:event) do
+ FactoryGirl.create(:ems_event,
+ :event_type => 'CONTAINER_CREATED',
+ :ems_id => 6,
+ :container_group_name => 'hawkular-cassandra-1-wb1z6',
+ :container_namespace => 'openshift-infra',
+ :container_name => 'hawkular-cassandra-1',
+ :ext_management_system => ems)
+ end
+
+ flags = {:ems_cloud => false,
+ :ems_container => true,
+ :time_zone => nil}
+ tests = {'event_type' => 'test timeline',
+ 'ext_management_system.name' => 'test timeline',
+ 'container_node_name' => ''}
+
+ tests.each do |column, href|
+ it "Evaluate column #{column} content" do
+ row[column] = 'test timeline'
+ val = ReportFormatter::TimelineMessage.new(row, event, flags, 'EmsEvent').message_html(column)
+ expect(val).to eq(href)
+ end
+ end
+ end
+
+ describe '#message_html on vm event' do
+ row = {}
+ let(:vm) { FactoryGirl.create(:vm_redhat, :id => 42) }
+ let(:event) do
+ FactoryGirl.create(:ems_event,
+ :event_type => 'VM_CREATED',
+ :vm_or_template => vm)
+ end
+
+ flags = {:ems_cloud => false,
+ :ems_container => false,
+ :time_zone => nil}
+ tests = {'event_type' => 'test timeline',
+ 'ext_management_system.name' => '',
+ 'src_vm_name' => 'test timeline'}
+
+ tests.each do |column, href|
+ it "Evaluate column #{column} content" do
+ row[column] = 'test timeline'
+ val = ReportFormatter::TimelineMessage.new(row, event, flags, 'EmsEvent').message_html(column)
+ expect(val).to eq(href)
+ end
+ end
+ end
+
+ describe '#message_html on bottleneck event' do
+ row = {}
+ let(:ems_cluster) { FactoryGirl.create(:ems_cluster, :id => 42, :name => 'Test Cluster') }
+ let(:event) do
+ FactoryGirl.create(:bottleneck_event,
+ :event_type => 'MemoryUsage',
+ :resource_id => 42,
+ :resource_name => ems_cluster.name,
+ :resource_type => 'EmsCluster')
+ end
+
+ tests = {'event_type' => 'MemoryUsage',
+ 'resource_name' => 'Test Cluster'}
+
+ tests.each do |column, href|
+ it "Evaluate column #{column} content" do
+ row[column] = 'MemoryUsage'
+ val = ReportFormatter::TimelineMessage.new(row, event, {}, 'BottleneckEvent').message_html(column)
+ expect(val).to eq(href)
+ end
+ end
+ end
+
+ describe '#message_html on policy event' do
+ row = {}
+ let(:vm) { FactoryGirl.create(:vm_redhat, :id => 42, :name => 'Test VM') }
+ let(:event) do
+ FactoryGirl.create(:policy_event,
+ :event_type => 'vm_poweroff',
+ :target_id => 42,
+ :target_name => vm.name,
+ :target_class => 'VmOrTemplate')
+ end
+
+ tests = {'event_type' => 'vm_poweroff',
+ 'target_name' => 'Test VM
VM or Template: Test VM
Assigned Profiles: '}
+
+ tests.each do |column, href|
+ it "Evaluate column #{column} content" do
+ row[column] = 'vm_poweroff'
+ val = ReportFormatter::TimelineMessage.new(row, event, {}, 'PolicyEvent').message_html(column)
+ expect(val).to eq(href)
+ end
+ end
+ end
+
+ describe '#events count for different categories' do
+ def stub_ems_event(event_type)
+ ems = FactoryGirl.create(:ems_redhat)
+ ems_event = EventStream.create!(:event_type => event_type, :ems_id => ems.id)
+ ems_event
+ end
+
+ before do
+ @report = FactoryGirl.create(:miq_report,
+ :db => "EventStream",
+ :col_order => %w(id name event_type timestamp),
+ :headers => %w(id name event_type timestamp),
+ :timeline => {:field => "EmsEvent-timestamp", :position => "Last"})
+ @report.rpt_options = {:categories => {:power => {:display_name => "Power Activity",
+ :event_groups => %w(VmPoweredOffEvent VmPoweredOnEvent)},
+ :snapshot => {:display_name => "Snapshot Activity",
+ :event_groups => %w(AlarmCreatedEvent AlarmRemovedEvent)}}
+ }
+
+ data = []
+ 30.times do
+ data.push(Ruport::Data::Record.new("id" => stub_ems_event("VmPoweredOffEvent").id,
+ "name" => "Baz",
+ "event_type" => "VmPoweredOffEvent",
+ "timestamp" => Time.zone.now))
+ end
+
+ 15.times do
+ data.push(Ruport::Data::Record.new("id" => stub_ems_event("AlarmCreatedEvent").id,
+ "name" => "Baz",
+ "event_type" => "AlarmCreatedEvent",
+ "timestamp" => Time.zone.now))
+ end
+
+ @report.table = Ruport::Data::Table.new(
+ :column_names => %w(id name event_type timestamp),
+ :data => data
+ )
+ end
+
+ it 'shows correct count of timeline events based on categories' do
+ allow_any_instance_of(Ruport::Controller::Options).to receive(:mri).and_return(@report)
+ events = ReportFormatter::ReportTimeline.new.build_document_body
+ expect(JSON.parse(events)[0]["data"][0].length).to eq(30)
+ expect(JSON.parse(events)[1]["data"][0].length).to eq(15)
+ end
+
+ it 'shows correct count of timeline events together for report object with no categories' do
+ @report.rpt_options = {}
+ allow_any_instance_of(Ruport::Controller::Options).to receive(:mri).and_return(@report)
+ events = ReportFormatter::ReportTimeline.new.build_document_body
+ expect(JSON.parse(events)[0]["data"][0].length).to eq(45)
+ end
+
+ it 'shows correct count of timeline events for timeline based report when rpt_options is nil' do
+ @report.rpt_options = nil
+ allow_any_instance_of(Ruport::Controller::Options).to receive(:mri).and_return(@report)
+ events = ReportFormatter::ReportTimeline.new.build_document_body
+ expect(JSON.parse(events)[0]["data"][0].length).to eq(45)
+ end
+ end
+end