diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2011-03-30 16:02:04 +0200 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2011-03-30 16:02:04 +0200 |
commit | 7aa6f8fd79691c00056d901a367999ab565921b1 (patch) | |
tree | ee25c0bc67259ac4bf8b524f83dc732b27ae3711 /sonar-server | |
parent | d40e548351976116800808778dc82b38f620f55e (diff) | |
download | sonarqube-7aa6f8fd79691c00056d901a367999ab565921b1.tar.gz sonarqube-7aa6f8fd79691c00056d901a367999ab565921b1.zip |
SONAR-1332 profile comparison tool
Diffstat (limited to 'sonar-server')
-rw-r--r-- | sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb | 160 | ||||
-rw-r--r-- | sonar-server/src/main/webapp/WEB-INF/app/helpers/profiles_helper.rb | 12 | ||||
-rw-r--r-- | sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_diff_rule.html.erb | 3 | ||||
-rw-r--r-- | sonar-server/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb | 136 | ||||
-rw-r--r-- | sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb | 11 | ||||
-rw-r--r-- | sonar-server/src/main/webapp/images/compare.png | bin | 0 -> 593 bytes | |||
-rw-r--r-- | sonar-server/src/main/webapp/images/switch.png | bin | 0 -> 3223 bytes | |||
-rw-r--r-- | sonar-server/src/main/webapp/stylesheets/style.css | 27 |
8 files changed, 344 insertions, 5 deletions
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb index 1f068161adf..24339483e4a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb @@ -197,7 +197,8 @@ class ProfilesController < ApplicationController @select_parent = [['None', nil]] + profiles.collect{ |profile| [profile.name, profile.name] } end - + + # # # POST /profiles/change_parent?id=<profile id>&parent_name=<parent profile name> @@ -282,8 +283,165 @@ class ProfilesController < ApplicationController end + # + # + # GET /profiles/compare?id1=<profile1 id>&id2=<profile2 id> + # + # + def compare + @profiles = Profile.find(:all, :conditions => ['enabled=?', true], :order => 'language asc, name') + if params[:id1].present? && params[:id2].present? + @profile1 = Profile.find(params[:id1]) + @profile2 = Profile.find(params[:id2]) + + arules1 = ActiveRule.find(:all, :include => [{:active_rule_parameters => :rules_parameter}, :rule], + :conditions => ['active_rules.profile_id=?', @profile1.id]) + arules2 = ActiveRule.find(:all, :order => 'rules.plugin_name, rules.plugin_rule_key', :include => [{:active_rule_parameters => :rules_parameter}, :rule], + :conditions => ['active_rules.profile_id=?', @profile2.id]) + + diffs_by_rule={} + arules1.each do |arule1| + diffs_by_rule[arule1.rule]||=RuleDiff.new(arule1.rule) + diffs_by_rule[arule1.rule].arule1=arule1 + end + arules2.each do |arule2| + diffs_by_rule[arule2.rule]||=RuleDiff.new(arule2.rule) + diffs_by_rule[arule2.rule].arule2=arule2 + end + @in1=[] + @in2=[] + @modified=[] + @sames=[] + diffs_by_rule.values.sort.each do |diff| + case diff.status + when DIFF_IN1: @in1<<diff + when DIFF_IN2: @in2<<diff + when DIFF_MODIFIED: @modified<<diff + when DIFF_SAME: @sames<<diff + end + end + end + end + + DIFF_IN1=1 + DIFF_IN2=2 + DIFF_MODIFIED=3 + DIFF_SAME=4 + private + class RuleDiff + attr_reader :rule, :removed_params, :added_params + attr_accessor :arule1, :arule2 + + def initialize(rule) + @rule=rule + end + + def status + @status ||= + begin + if @arule1.nil? + @status=(@arule2 ? DIFF_IN2 : nil) + else + if @arule2 + # compare severity and parameters + @removed_params=[] + @added_params=[] + @rule.parameters.each do |param| + v1=@arule1.value(param.id) + v2=@arule2.value(param.id) + if v1 + if v2 + if v1!=v2 + @removed_params<<@arule1.parameter(param.name) + @added_params<<@arule2.parameter(param.name) + end + else + @removed_params<<@arule1.parameter(param.name) + end + elsif v2 + @added_params<<@arule2.parameter(param.name) + end + end + diff=(@arule1.priority!=@arule2.priority) || !@removed_params.empty? || !@added_params.empty? + @status=(diff ? DIFF_MODIFIED : DIFF_SAME) + else + @status=DIFF_IN1 + end + end + end + end + + def <=>(other) + rule.name()<=>other.rule.name + end + end + + # + # Remove active rules that are identical in both collections (same severity and same parameters) + # and return a map with results {:added => X, :removed => Y, :modified => Z, + # :rules => {rule1 => [activeruleleft1, activeruleright1], rule2 => [activeruleleft2, nil], ...]} + # Assume both collections are ordered by rule key + # + def compute_diff(arules1, arules2) + rules = {} + removed = 0 + added = 0 + modified = 0 + same = 0 + begin + diff = false + #take first item of each collection + active_rule1 = arules1.first + active_rule2 = arules2.first + if active_rule1 != nil and active_rule2 != nil + order = active_rule1.rule.key <=> active_rule2.rule.key + if order < 0 + active_rule2 = nil + rule = active_rule1.rule + diff = true + removed = removed +1 + elsif order > 0 + active_rule1 = nil + rule = active_rule2.rule + diff = true + added = added +1 + else + rule = active_rule1.rule # = active_rule2.rule + #compare severity + diff = true if active_rule1.priority != active_rule2.priority + #compare parameters + rule.parameters.each do |param| + diff = true if active_rule1.value(param.id) != active_rule2.value(param.id) + end + if diff + modified = modified + 1 + else + same = same +1 + end + end + elsif active_rule1 != nil + #no more rule in right collection + diff = true + removed = removed +1 + rule = active_rule1.rule + elsif active_rule2 != nil + #no more rule in left collection + diff = true + added = added +1 + rule = active_rule2.rule + end + # remove processed rule(s) + arules1 = arules1.drop(1) if active_rule1 != nil + arules2 = arules2.drop(1) if active_rule2 != nil + if diff + rules[rule] = [active_rule1, active_rule2] + end + end while !arules1.empty? || !arules2.empty? + return {:same => same, :added => added, :removed => removed, :modified => modified, :rules => rules} + end + def read_file_param(configuration_file) # configuration file is a StringIO if configuration_file.respond_to?(:read) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/profiles_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/profiles_helper.rb index d9e3d3a5db5..3c2146fb835 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/profiles_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/profiles_helper.rb @@ -32,4 +32,16 @@ module ProfilesHelper end label end + + def options_for_profiles(profiles, selected_id=nil) + html="" + profiles.group_by(&:language).each do |language, profiles| + html += "<optgroup label=\"#{html_escape(language)}\">" + profiles.each do |profile| + html += "<option value='#{profile.id}' #{'selected' if profile.id==selected_id}>#{html_escape(profile.name)}</option>" + end + html += "</optgroup>" + end + html + end end
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_diff_rule.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_diff_rule.html.erb new file mode 100644 index 00000000000..9b7acaec2d7 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_diff_rule.html.erb @@ -0,0 +1,3 @@ +<%= image_tag "priority/#{arule.priority}.png" %> + +<span class="rulename"><a onclick="window.open(this.href,'rule','height=700,width=500,scrollbars=1,resizable=1');return false;" href="<%= url_for :controller => 'rules', :action => 'show', :id => arule.rule.key, :layout => 'false' -%>"><%= h(arule.rule.name) -%></a></span> <em><%= h(arule.rule.plugin_name) -%></em>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb new file mode 100644 index 00000000000..70235457779 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb @@ -0,0 +1,136 @@ +<h1 class="marginbottom10"><%= link_to 'Quality profiles', :controller => 'profiles', :action => 'index' -%> / Compare</h1> + +<form method="GET" class="marginbottom10"> + <select name="id1" class="small"> + <option value=""></option> + <%= options_for_profiles(@profiles, params[:id1].to_i) %> + </select> + + <select name="id2" class="small"> + <option value=""></option> + <%= options_for_profiles(@profiles, params[:id2].to_i) %> + </select> + <input type="submit" value="Compare" class="small"/> + <% if @profile1 && @profile2 %> + <a href="<%= url_for :action => 'compare', :id1 => @profile2.id, :id2 => @profile1.id -%>" id="switch" title="Switch left and right view"><%= image_tag 'switch.png'-%></a> + <% end %> +</form> + +<% if @profile1 && @profile2 %> + +<table class="width100 marginbottom10" id="profile_diff_table"> +<tbody> + +<% unless @in1.empty? %> + <tr> + <td width="49%"> + <table class="data width100 marginbottom10"> + <thead> + <tr> + <th><%= @in1.size -%> rules only in <a href="<%= url_for :controller => 'rules_configuration', :action => 'index', :id => @profile1.id -%>"><%= h @profile1.name %></a></th> + </tr> + </thead> + <% @in1.each do |diff| %> + <tr id="rule_<%= u(diff.rule.key) -%>"> + <td class="<%= cycle('even','odd', :name => 'in1')-%>"> + <%= render :partial => 'diff_rule', :locals => {:arule => diff.arule1} %> + </td> + </tr> + <% end %> + </table> + </td> + <td width="2%"> </td> + <td width="49%"> </td> + </tr> +<% end %> + +<% unless @in2.empty? %> + <tr> + <td width="49%"></td> + <td width="2%"> </td> + <td width="49%"> + <table class="data width100 marginbottom10"> + <thead> + <tr> + <th><%= @in2.size -%> rules only in <a href="<%= url_for :controller => 'rules_configuration', :action => 'index', :id => @profile2.id -%>"><%= h @profile2.name %></a></th> + </tr> + </thead> + <% @in2.each do |diff| %> + <tr id="rule_<%= u(diff.rule.key) -%>"> + <td class="<%= cycle('even','odd', :name => 'in2')-%>"> + <%= render :partial => 'diff_rule', :locals => {:arule => diff.arule2} %> + </td> + </tr> + <% end %> + </table> + </td> + </tr> +<% end %> + + +<% unless @modified.empty? %> +<tr> + <td colspan="3"> + <table class="data width100 marginbottom10"> + <thead> + <tr> + <th width="49%"><%= @modified.size -%> rules have a different configuration<br/> + <a href="<%= url_for :controller => 'rules_configuration', :action => 'index', :id => @profile1.id -%>"><%= h @profile1.name %></a> + </th> + <th width="2%"></th> + <th width="49%"><br/><a href="<%= url_for :controller => 'rules_configuration', :action => 'index', :id => @profile2.id -%>"><%= h @profile2.name %></a></th> + </tr> + </thead> + <% @modified.each do |diff| + td_css=cycle('even','odd', :name => 'modified') + %> + <tr id="rule_<%= u(diff.rule.key) -%>"> + <td class="<%= td_css -%>" width="49%"> + <%= render :partial => 'diff_rule', :locals => {:arule => diff.arule1} %> + <% if diff.removed_params && !diff.removed_params.empty? %> + <ul> + <% diff.removed_params.each do |parameter| %> + <li><%= h(parameter.name) -%>: <span class="diffParam"><%= parameter.value.gsub(',', ', ') -%></span></li> + <% end %> + </ul> + <% end %> + </td> + <td width="2%" class="<%= td_css -%>"> </td> + <td class="<%= td_css -%>" width="49%"> + <%= render :partial => 'diff_rule', :locals => {:arule => diff.arule2} %> + <% if diff.added_params && !diff.added_params.empty? %> + <ul> + <% diff.added_params.each do |parameter| %> + <li><%= h(parameter.name) -%>: <span class="diffParam"><%= parameter.value.gsub(',', ', ') -%></span></li> + <% end %> + </ul> + <% end %> + </td> + </tr> + <% end %> + </table> + </td> + </tr> +<% end %> + +<% unless @sames.empty? %> +<tr> + <td colspan="3"> + <table class="data width100 marginbottom10"> + <thead> + <tr> + <th><%= @sames.size -%> rules have the same configuration</th> + </tr> + </thead> + <tbody> + <tr> + <td class="even">Not displayed</td> + </tr> + </tbody> + </table> + </td> + </tr> +<% end %> +</tbody> +</table> +<% end %>
\ No newline at end of file diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb index 48cb7947eb3..c3d2cf5104e 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb @@ -1,7 +1,12 @@ <% if administrator? %> <div class="line-block marginbottom10"> - <ul style="float: right" class="horizontal"> - <li class="marginleft10 restore"> + <ul class="operations"> + <li> + <%= image_tag 'compare.png' -%> + <a href="<%= ApplicationController.root_context-%>/profiles/compare" id="compare-link">Compare profiles</a> + </li> + <li class="last"> + <%= image_tag 'restore.gif' -%> <a href="#" onclick="$('restore-form').show();return false;" id="restore-link">Restore profile</a> </li> </ul> @@ -15,7 +20,7 @@ <tr> <td colspan="2"> - <input type="submit" value="Restore profile"></input> + <input type="submit" value="Restore profile"/> <a href="#" onclick="$('restore-form').reset();$('restore-form').hide();return false;">Cancel</a> </td> </tr> diff --git a/sonar-server/src/main/webapp/images/compare.png b/sonar-server/src/main/webapp/images/compare.png Binary files differnew file mode 100644 index 00000000000..a61ec134324 --- /dev/null +++ b/sonar-server/src/main/webapp/images/compare.png diff --git a/sonar-server/src/main/webapp/images/switch.png b/sonar-server/src/main/webapp/images/switch.png Binary files differnew file mode 100644 index 00000000000..4a72a34f9a5 --- /dev/null +++ b/sonar-server/src/main/webapp/images/switch.png diff --git a/sonar-server/src/main/webapp/stylesheets/style.css b/sonar-server/src/main/webapp/stylesheets/style.css index e4e37fd9e39..435d6dd9fc4 100644 --- a/sonar-server/src/main/webapp/stylesheets/style.css +++ b/sonar-server/src/main/webapp/stylesheets/style.css @@ -255,6 +255,11 @@ a { .left { text-align: left; } + +.center { + text-align: center; +} + code { font-size: 93%; } @@ -284,6 +289,12 @@ code { .small { font-size: 85%; } +em { + color: #AAA; + font-size: 85%; + font-style: normal; +} + a.external { background: url('../images/links/external.png') no-repeat 100% 0; padding: 0 16px 0px 0; @@ -603,6 +614,10 @@ ul.operations li.last { ul.operations li a { color: #555; } +ul.operations li img { + vertical-align: middle; + margin-right: 5px; +} /* RESOURCE VIEWER */ .resourceName h1 { @@ -1582,4 +1597,14 @@ table.nowrap td, td.nowrap { display: block; background: url("../images/restore.gif") no-repeat scroll left 50% transparent; padding: 2px 0 2px 20px; -}
\ No newline at end of file +} +.compare { + display: block; + background: url("../images/compare.png") no-repeat scroll left 50% transparent; + padding: 2px 0 2px 20px; +} + +/* Profile diff */ +.diffParam { + font-family: 'Bitstream Vera Sans Mono','Courier',monospace; +} |