]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5329 Very first draft of new Quality Profile changelog
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 19 Jun 2014 18:12:14 +0000 (20:12 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 19 Jun 2014 18:16:44 +0000 (20:16 +0200)
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-server/src/main/java/org/sonar/server/activity/RubyActivityService.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java
sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivityQuery.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/profiles/_tabs.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/profiles/changelog.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/profiles/changelog2.html.erb [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/activity/RubyActivityServiceTest.java [new file with mode: 0644]

index a734b36f0eb1346f5511c748c358446b06151b13..5d0a530435187583af51aee34038e45668dfd565 100644 (file)
@@ -1587,8 +1587,7 @@ quality_profiles.projects_warning=List of projects explicitly associated to this
 quality_profiles.including_x_overriding.suffix=, incl. {0} overriding
 quality_profiles.set_parent=Set parent
 quality_profiles.inherit_rules_from_profile=Inherit rules configuration from the profile
-quality_profiles.not_used=This Quality Profile has not yet been used, so change tracking is not in use yet.
-quality_profiles.first_use_without_change=No changes have occurred since first use of this Quality Profile.
+quality_profiles.changelog.empty=No changes have been done.
 quality_profiles.changelog_from=Changelog from
 quality_profiles.no_version=no version
 quality_profiles.last_version_x_with_date=last version {0} ({1})
diff --git a/sonar-server/src/main/java/org/sonar/server/activity/RubyActivityService.java b/sonar-server/src/main/java/org/sonar/server/activity/RubyActivityService.java
new file mode 100644 (file)
index 0000000..43b98ea
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.activity;
+
+import org.picocontainer.Startable;
+import org.sonar.api.ServerComponent;
+import org.sonar.server.qualityprofile.QProfileActivity;
+import org.sonar.server.qualityprofile.QProfileActivityQuery;
+import org.sonar.server.qualityprofile.QProfileService;
+import org.sonar.server.search.QueryOptions;
+import org.sonar.server.util.RubyUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @deprecated in 4.4 because Ruby on Rails is deprecated too !
+ */
+@Deprecated
+public class RubyActivityService implements ServerComponent, Startable {
+
+  private final QProfileService service;
+
+  public RubyActivityService(QProfileService service) {
+    this.service = service;
+  }
+
+  /**
+   * Used in profiles_controller.rb
+   */
+  public List<QProfileActivity> search(Map<String, Object> params) {
+    QProfileActivityQuery query = new QProfileActivityQuery();
+    List<String> profileKeys = RubyUtils.toStrings(params.get("profileKeys"));
+    if (profileKeys != null) {
+      query.setQprofileKeys(profileKeys);
+    }
+    Date since = RubyUtils.toDate(params.get("since"));
+    if (since != null) {
+      query.setSince(since);
+    }
+    Date to = RubyUtils.toDate(params.get("to"));
+    if (to != null) {
+      query.setTo(to);
+    }
+    return service.findActivities(query, new QueryOptions().setMaxLimit());
+  }
+
+  @Override
+  public void start() {
+    // used to force pico to instantiate the singleton at startup
+  }
+
+  @Override
+  public void stop() {
+    // implement startable
+  }
+}
index c5d1108e2a34773b0ed9f81630e3f75b2d9b23dd..d38c0cb6eb1f1f589f0e2005b38ffc0369b26a15 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.activity.index;
 import org.apache.commons.lang.builder.ReflectionToStringBuilder;
 import org.sonar.core.activity.Activity;
 import org.sonar.server.search.BaseDoc;
+import org.sonar.server.search.IndexUtils;
 
 import java.util.Date;
 import java.util.Map;
@@ -37,27 +38,27 @@ public class ActivityDoc extends BaseDoc implements Activity {
 
   @Override
   public Date time() {
-    return this.getField(ActivityNormalizer.LogFields.CREATED_AT.field());
+    return IndexUtils.parseDateTime((String) getField(ActivityNormalizer.LogFields.CREATED_AT.field()));
   }
 
   @Override
   public String author() {
-    return this.getField(ActivityNormalizer.LogFields.AUTHOR.field());
+    return this.getNullableField(ActivityNormalizer.LogFields.AUTHOR.field());
   }
 
   @Override
   public String action() {
-    return this.getField(ActivityNormalizer.LogFields.ACTION.field());
+    return this.getNullableField(ActivityNormalizer.LogFields.ACTION.field());
   }
 
   @Override
   public Map<String, String> details() {
-    return this.getField(ActivityNormalizer.LogFields.DETAILS.field());
+    return this.getNullableField(ActivityNormalizer.LogFields.DETAILS.field());
   }
 
   @Override
   public String message() {
-    return this.getField(ActivityNormalizer.LogFields.MESSAGE.field());
+    return this.getNullableField(ActivityNormalizer.LogFields.MESSAGE.field());
   }
 
   @Override
index fc892897c6e42112759ac741395bbbb87db60db4..40685bd9646ba7a430fcb3544aa76bf655f524b3 100644 (file)
@@ -52,13 +52,7 @@ import org.sonar.core.measure.db.MeasureFilterDao;
 import org.sonar.core.metric.DefaultMetricFinder;
 import org.sonar.core.notification.DefaultNotificationManager;
 import org.sonar.core.permission.PermissionFacade;
-import org.sonar.core.persistence.DaoUtils;
-import org.sonar.core.persistence.DatabaseVersion;
-import org.sonar.core.persistence.DefaultDatabase;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.persistence.PreviewDatabaseFactory;
-import org.sonar.core.persistence.SemaphoreUpdater;
-import org.sonar.core.persistence.SemaphoresImpl;
+import org.sonar.core.persistence.*;
 import org.sonar.core.preview.PreviewCache;
 import org.sonar.core.profiling.Profiling;
 import org.sonar.core.purge.PurgeProfiler;
@@ -78,6 +72,7 @@ import org.sonar.jpa.session.DatabaseSessionProvider;
 import org.sonar.jpa.session.DefaultDatabaseConnector;
 import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory;
 import org.sonar.server.activity.ActivityService;
+import org.sonar.server.activity.RubyActivityService;
 import org.sonar.server.activity.db.ActivityDao;
 import org.sonar.server.activity.index.ActivityIndex;
 import org.sonar.server.activity.index.ActivityNormalizer;
@@ -96,33 +91,11 @@ import org.sonar.server.db.DbClient;
 import org.sonar.server.db.EmbeddedDatabaseFactory;
 import org.sonar.server.db.migrations.DatabaseMigrations;
 import org.sonar.server.db.migrations.DatabaseMigrator;
-import org.sonar.server.debt.DebtCharacteristicsXMLImporter;
-import org.sonar.server.debt.DebtModelBackup;
-import org.sonar.server.debt.DebtModelLookup;
-import org.sonar.server.debt.DebtModelOperations;
-import org.sonar.server.debt.DebtModelPluginRepository;
-import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.debt.DebtModelXMLExporter;
-import org.sonar.server.debt.DebtRulesXMLImporter;
+import org.sonar.server.debt.*;
 import org.sonar.server.duplication.ws.DuplicationsParser;
 import org.sonar.server.duplication.ws.DuplicationsWriter;
 import org.sonar.server.duplication.ws.DuplicationsWs;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.AssignAction;
-import org.sonar.server.issue.CommentAction;
-import org.sonar.server.issue.DefaultIssueFinder;
-import org.sonar.server.issue.InternalRubyIssueService;
-import org.sonar.server.issue.IssueBulkChangeService;
-import org.sonar.server.issue.IssueChangelogFormatter;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueCommentService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.issue.IssueStatsFinder;
-import org.sonar.server.issue.PlanAction;
-import org.sonar.server.issue.PublicRubyIssueService;
-import org.sonar.server.issue.ServerIssueStorage;
-import org.sonar.server.issue.SetSeverityAction;
-import org.sonar.server.issue.TransitionAction;
+import org.sonar.server.issue.*;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.actionplan.ActionPlanWs;
 import org.sonar.server.issue.filter.IssueFilterService;
@@ -147,83 +120,22 @@ import org.sonar.server.platform.ws.L10nWs;
 import org.sonar.server.platform.ws.RestartHandler;
 import org.sonar.server.platform.ws.ServerWs;
 import org.sonar.server.platform.ws.SystemWs;
-import org.sonar.server.plugins.BatchWs;
-import org.sonar.server.plugins.InstalledPluginReferentialFactory;
-import org.sonar.server.plugins.PluginDownloader;
-import org.sonar.server.plugins.ServerExtensionInstaller;
-import org.sonar.server.plugins.ServerPluginJarInstaller;
-import org.sonar.server.plugins.ServerPluginJarsInstaller;
-import org.sonar.server.plugins.ServerPluginRepository;
-import org.sonar.server.plugins.UpdateCenterClient;
-import org.sonar.server.plugins.UpdateCenterMatrixFactory;
+import org.sonar.server.plugins.*;
 import org.sonar.server.qualitygate.QgateProjectFinder;
 import org.sonar.server.qualitygate.QualityGates;
 import org.sonar.server.qualitygate.RegisterQualityGates;
-import org.sonar.server.qualitygate.ws.QGatesAppAction;
-import org.sonar.server.qualitygate.ws.QGatesCopyAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeselectAction;
-import org.sonar.server.qualitygate.ws.QGatesDestroyAction;
-import org.sonar.server.qualitygate.ws.QGatesListAction;
-import org.sonar.server.qualitygate.ws.QGatesRenameAction;
-import org.sonar.server.qualitygate.ws.QGatesSearchAction;
-import org.sonar.server.qualitygate.ws.QGatesSelectAction;
-import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesShowAction;
-import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesWs;
-import org.sonar.server.qualityprofile.BuiltInProfiles;
-import org.sonar.server.qualityprofile.QProfileBackuper;
-import org.sonar.server.qualityprofile.QProfileCopier;
-import org.sonar.server.qualityprofile.QProfileFactory;
-import org.sonar.server.qualityprofile.QProfileLookup;
-import org.sonar.server.qualityprofile.QProfileOperations;
-import org.sonar.server.qualityprofile.QProfileProjectLookup;
-import org.sonar.server.qualityprofile.QProfileProjectOperations;
-import org.sonar.server.qualityprofile.QProfileRepositoryExporter;
-import org.sonar.server.qualityprofile.QProfileReset;
-import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.qualityprofile.QProfiles;
-import org.sonar.server.qualityprofile.RegisterQualityProfiles;
-import org.sonar.server.qualityprofile.RuleActivator;
-import org.sonar.server.qualityprofile.RuleActivatorContextFactory;
+import org.sonar.server.qualitygate.ws.*;
+import org.sonar.server.qualityprofile.*;
 import org.sonar.server.qualityprofile.db.ActiveRuleDao;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
-import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions;
-import org.sonar.server.qualityprofile.ws.ProfilesWs;
-import org.sonar.server.qualityprofile.ws.QProfileRestoreBuiltInAction;
-import org.sonar.server.qualityprofile.ws.QProfilesWs;
-import org.sonar.server.qualityprofile.ws.RuleActivationActions;
-import org.sonar.server.rule.DeprecatedRulesDefinition;
-import org.sonar.server.rule.RegisterRules;
-import org.sonar.server.rule.RubyRuleService;
-import org.sonar.server.rule.RuleCreator;
-import org.sonar.server.rule.RuleDefinitionsLoader;
-import org.sonar.server.rule.RuleDeleter;
-import org.sonar.server.rule.RuleOperations;
-import org.sonar.server.rule.RuleRepositories;
-import org.sonar.server.rule.RuleService;
-import org.sonar.server.rule.RuleUpdater;
+import org.sonar.server.qualityprofile.ws.*;
+import org.sonar.server.rule.*;
 import org.sonar.server.rule.db.RuleDao;
 import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.rule.index.RuleNormalizer;
-import org.sonar.server.rule.ws.ActiveRuleCompleter;
-import org.sonar.server.rule.ws.AppAction;
-import org.sonar.server.rule.ws.DeleteAction;
-import org.sonar.server.rule.ws.RuleMapping;
-import org.sonar.server.rule.ws.RulesWebService;
-import org.sonar.server.rule.ws.SearchAction;
-import org.sonar.server.rule.ws.TagsAction;
-import org.sonar.server.rule.ws.UpdateAction;
-import org.sonar.server.search.ESNode;
-import org.sonar.server.search.IndexClient;
-import org.sonar.server.search.IndexQueue;
-import org.sonar.server.search.IndexQueueWorker;
-import org.sonar.server.search.IndexSynchronizer;
+import org.sonar.server.rule.ws.*;
+import org.sonar.server.search.*;
 import org.sonar.server.source.CodeColorizers;
 import org.sonar.server.source.DeprecatedSourceDecorator;
 import org.sonar.server.source.HtmlSourceDecorator;
@@ -232,27 +144,9 @@ import org.sonar.server.source.ws.ScmAction;
 import org.sonar.server.source.ws.ScmWriter;
 import org.sonar.server.source.ws.ShowAction;
 import org.sonar.server.source.ws.SourcesWs;
-import org.sonar.server.startup.CleanPreviewAnalysisCache;
-import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules;
-import org.sonar.server.startup.GeneratePluginIndex;
-import org.sonar.server.startup.GwtPublisher;
-import org.sonar.server.startup.JdbcDriverDeployer;
-import org.sonar.server.startup.LogServerId;
-import org.sonar.server.startup.RegisterDashboards;
-import org.sonar.server.startup.RegisterDebtModel;
-import org.sonar.server.startup.RegisterMetrics;
-import org.sonar.server.startup.RegisterNewMeasureFilters;
-import org.sonar.server.startup.RegisterPermissionTemplates;
-import org.sonar.server.startup.RegisterServletFilters;
-import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
-import org.sonar.server.startup.ServerMetadataPersister;
+import org.sonar.server.startup.*;
 import org.sonar.server.test.CoverageService;
-import org.sonar.server.test.ws.CoverageShowAction;
-import org.sonar.server.test.ws.CoverageWs;
-import org.sonar.server.test.ws.TestsCoveredFilesAction;
-import org.sonar.server.test.ws.TestsShowAction;
-import org.sonar.server.test.ws.TestsTestCasesAction;
-import org.sonar.server.test.ws.TestsWs;
+import org.sonar.server.test.ws.*;
 import org.sonar.server.text.MacroInterpreter;
 import org.sonar.server.text.RubyTextService;
 import org.sonar.server.ui.JRubyI18n;
@@ -260,20 +154,9 @@ import org.sonar.server.ui.JRubyProfiling;
 import org.sonar.server.ui.PageDecorations;
 import org.sonar.server.ui.Views;
 import org.sonar.server.updatecenter.ws.UpdateCenterWs;
-import org.sonar.server.user.DefaultUserService;
-import org.sonar.server.user.DoPrivileged;
-import org.sonar.server.user.GroupMembershipFinder;
-import org.sonar.server.user.GroupMembershipService;
-import org.sonar.server.user.NewUserNotifier;
-import org.sonar.server.user.SecurityRealmFactory;
+import org.sonar.server.user.*;
 import org.sonar.server.user.ws.UsersWs;
-import org.sonar.server.util.BooleanTypeValidation;
-import org.sonar.server.util.FloatTypeValidation;
-import org.sonar.server.util.IntegerTypeValidation;
-import org.sonar.server.util.StringListTypeValidation;
-import org.sonar.server.util.StringTypeValidation;
-import org.sonar.server.util.TextTypeValidation;
-import org.sonar.server.util.TypeValidations;
+import org.sonar.server.util.*;
 import org.sonar.server.ws.ListingWs;
 import org.sonar.server.ws.WebServiceEngine;
 
@@ -437,6 +320,7 @@ class ServerComponents {
     pico.addSingleton(QProfileCopier.class);
     pico.addSingleton(QProfileBackuper.class);
     pico.addSingleton(QProfileReset.class);
+    pico.addSingleton(RubyActivityService.class);
 
     // rule
     pico.addSingleton(AnnotationRuleParser.class);
index 716339c303df5d2841155e72380a92d1fa67f01d..cc303dab5a1525ea99ca68dd3cd48179a6c12ed8 100644 (file)
@@ -31,7 +31,6 @@ import java.util.Collection;
  */
 public class QProfileActivityQuery extends ActivityQuery {
 
-
   Collection<String> qprofileKeys;
 
   public QProfileActivityQuery() {
@@ -44,7 +43,8 @@ public class QProfileActivityQuery extends ActivityQuery {
     return qprofileKeys;
   }
 
-  public void setQprofileKeys(Collection<String> qprofileKeys) {
+  public QProfileActivityQuery setQprofileKeys(Collection<String> qprofileKeys) {
     this.qprofileKeys = qprofileKeys;
+    return this;
   }
 }
index 0f265b097df946f15c9c8a1404d0ba6306fba014..48e6f65be917da1c5014764491eddcc792dd60e1 100644 (file)
@@ -285,6 +285,18 @@ class ProfilesController < ApplicationController
     set_profile_breadcrumbs
   end
 
+  # GET /profiles/changelog?id=<profile id>
+  def changelog2
+    require_parameters 'id'
+
+    @profile = Internal.quality_profiles.profile(params[:id].to_i)
+    #search = {'profileKeys' => @profile.key().to_s, 'since' => params[:since], 'to' => params[:to]}
+    # search = {'profileKeys' => @profile.key().to_s}
+    @changes = Internal.component(Java::OrgSonarServerActivity::RubyActivityService.java_class).search({})
+
+    set_profile_breadcrumbs
+  end
+
   #
   #
   # GET /profiles/permalinks?id=<profile id>
index 0e686faa0d6b8b9fabf2940db1a748391c54bd69..e7efb2952ac47ed9ea7db55b23d14081bce6d06d 100644 (file)
@@ -18,6 +18,9 @@
   <li>
     <a href="<%= url_for :controller => 'profiles', :action => 'changelog', :id => @profile.id -%>" <%= "class='selected'" if selected_tab=='changelog' -%> id="tab-changelog"><%= message('changelog') -%></a>
   </li>
+  <li>
+    <a href="<%= url_for :controller => 'profiles', :action => 'changelog2', :id => @profile.id -%>" id="tab-changelog">Changelog2</a>
+  </li>
   <% if new_tab %>
   <li>
     <a href="#" class='selected'><%= new_tab -%></a>
index 288b3ec1dc01791d9f0b063c25b3f0dfe9b4aa52..5817dbeb35718646f539c7dfc5d57a5f6a7da530 100644 (file)
@@ -69,7 +69,6 @@
              else %>
              <%= message('quality_profiles.parameter_changed_from_x_to_x', :params => [param_change.name, param_change.old_value, param_change.new_value]) -%>
           <% end %>
-          <%= "<br/>" unless param_change == change.parameters.last %>
         <% end%>
       </td>
     </tr>
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/changelog2.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/profiles/changelog2.html.erb
new file mode 100644 (file)
index 0000000..2a563d4
--- /dev/null
@@ -0,0 +1,41 @@
+<div class="page">
+<%= render :partial => 'profiles/tabs', :locals => {:selected_tab=>'changelog'} %>
+
+<div class="tabs-panel marginbottom10">
+  <% if @changes.empty? %>
+    <%= message('quality_profiles.changelog.empty') -%>
+  <% else %>
+    <form class="marginbottom10" method="post" action="<%= ApplicationController.root_context %>/profiles/changelog">
+      <input name="id" type="hidden" value="<%= @profile.id %>"/>
+      <%= message('quality_profiles.changelog_from') -%>
+      <input name="since" type="text" value="<%=  %>"/>
+      <%= message('to').downcase -%>
+      <input name="to" type="text" value="<%=  %>"/>
+      <input type="submit" value="<%= h message('load_verb') -%>" id="submit"/>
+    </form>
+
+    <table id="profile-changelog" class="data width100">
+      <thead>
+        <tr>
+          <th><%= message('date') -%></th>
+          <th><%= message('user') -%></th>
+          <th><%= message('action') -%></th>
+          <th><%= message('rule') -%></th>
+          <th><%= message('parameters') -%></th>
+        </tr>
+      </thead>
+      <%
+        @changes.each do |change|
+      %>
+      <tr class="<%= cycle('even', 'odd') -%>">
+        <td valign="top" width="1%" nowrap><%= change.time().to_s -%></td>
+        <td valign="top" width="1%" nowrap><%= change.author() ? change.author() : 'System' %></td>
+        <td valign="top" width="1%" nowrap><%= change.action() %></td>
+        <td valign="top"><%= change.ruleKey() %></td>
+      </tr>
+      <% end %>
+    </table>
+
+  <% end %>
+</div>
+</div>
diff --git a/sonar-server/src/test/java/org/sonar/server/activity/RubyActivityServiceTest.java b/sonar-server/src/test/java/org/sonar/server/activity/RubyActivityServiceTest.java
new file mode 100644 (file)
index 0000000..c4617ba
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.activity;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.activity.Activity;
+import org.sonar.server.qualityprofile.QProfileActivityQuery;
+import org.sonar.server.qualityprofile.QProfileService;
+import org.sonar.server.search.QueryOptions;
+
+import java.util.Date;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RubyActivityServiceTest {
+
+  @Mock
+  QProfileService service;
+
+  @Captor
+  ArgumentCaptor<QProfileActivityQuery> activityArgumentCaptor;
+
+  @Captor
+  ArgumentCaptor<QueryOptions> queryOptionsArgumentCaptor;
+
+  RubyActivityService rubyActivityService;
+
+  @Before
+  public void setUp() throws Exception {
+    rubyActivityService = new RubyActivityService(service);
+  }
+
+  @Test
+  public void search() throws Exception {
+    Date since = DateUtils.parseDate("2014-05-19");
+    Date to = DateUtils.parseDate("2014-06-19");
+    rubyActivityService.search(ImmutableMap.<String, Object>of("profileKeys", "PROFILE_KEY", "since", since, "to", to));
+
+    verify(service).findActivities(activityArgumentCaptor.capture(), queryOptionsArgumentCaptor.capture());
+
+    assertThat(queryOptionsArgumentCaptor.getValue().getLimit()).isEqualTo(QueryOptions.MAX_LIMIT);
+
+    assertThat(activityArgumentCaptor.getValue().getQprofileKeys()).containsOnly("PROFILE_KEY");
+    assertThat(activityArgumentCaptor.getValue().getTypes()).containsOnly(Activity.Type.QPROFILE);
+    assertThat(activityArgumentCaptor.getValue().getSince()).isEqualTo(since);
+    assertThat(activityArgumentCaptor.getValue().getTo()).isEqualTo(to);
+  }
+
+  @Test
+  public void search_with_empty_fields() throws Exception {
+    rubyActivityService.search(ImmutableMap.<String, Object>of());
+
+    verify(service).findActivities(activityArgumentCaptor.capture(), queryOptionsArgumentCaptor.capture());
+
+    assertThat(queryOptionsArgumentCaptor.getValue().getLimit()).isEqualTo(QueryOptions.MAX_LIMIT);
+
+    assertThat(activityArgumentCaptor.getValue().getQprofileKeys()).isEmpty();
+    assertThat(activityArgumentCaptor.getValue().getSince()).isNull();
+    assertThat(activityArgumentCaptor.getValue().getTo()).isNull();
+  }
+}