]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4301 Replace sql sort on issues by Java sort
authorJulien Lancelot <julien.lancelot@gmail.com>
Wed, 22 May 2013 13:13:36 +0000 (15:13 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Wed, 22 May 2013 13:13:45 +0000 (15:13 +0200)
sonar-application/pom.xml
sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java [new file with mode: 0644]

index ec958e9b814545e1caeb222a5a7adf8a01d2ac36..388bc4bb9cd7fe320ddc421c499068380de9b8de 100644 (file)
             <configuration>
               <rules>
                 <requireFilesSize>
-                  <maxsize>58100000</maxsize>
+                  <maxsize>59000000</maxsize>
                   <minsize>54000000</minsize>
                   <files>
                     <file>${project.build.directory}/sonar-${project.version}.zip</file>
index 5ffa8a844951e9d6179a98b7d22d5da880798fd6..ed15bef6208bc02ff3b33d83d35fbfd970ca5adc 100644 (file)
@@ -45,8 +45,6 @@ import static com.google.common.collect.Maps.newHashMap;
  */
 public class IssueDao implements BatchComponent, ServerComponent {
 
-  private final static Integer MAX_RESULT = 10000;
-
   private final MyBatis mybatis;
 
   public IssueDao(MyBatis mybatis) {
@@ -113,7 +111,7 @@ public class IssueDao implements BatchComponent, ServerComponent {
   }
 
   /**
-   * The returned IssueDto list contains only the issue id and the project id
+   * The returned IssueDto list contains only the issue id, the project id and the sort column
    */
   public List<IssueDto> selectIssueAndProjectIds(final IssueQuery query, final Collection<Integer> authorizedRootProjectIds, SqlSession session) {
     return selectIssueAndProjectIds(query, authorizedRootProjectIds, query.maxResults(), session);
@@ -138,24 +136,22 @@ public class IssueDao implements BatchComponent, ServerComponent {
   }
 
   @VisibleForTesting
-  Collection<IssueDto> selectByIds(Collection<Long> ids, IssueQuery.Sort sort, Boolean asc) {
+  Collection<IssueDto> selectByIds(Collection<Long> ids) {
     SqlSession session = mybatis.openSession();
     try {
-      return selectByIds(ids, sort, asc, session);
+      return selectByIds(ids, session);
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
 
-  public Collection<IssueDto> selectByIds(Collection<Long> ids, IssueQuery.Sort sort, Boolean asc, SqlSession session) {
+  public Collection<IssueDto> selectByIds(Collection<Long> ids, SqlSession session) {
     if (ids.isEmpty()) {
       return Collections.emptyList();
     }
     Object idsPartition = Lists.partition(newArrayList(ids), 1000);
     Map<String, Object> params = newHashMap();
     params.put("ids", idsPartition);
-    params.put("sort", sort);
-    params.put("asc", asc);
     return session.selectList("org.sonar.core.issue.db.IssueMapper.selectByIds", params);
   }
 }
index f82d0f9e7cf912eb89351f366e51321af192434c..52ae50afaa365e46af85ec2003bad1d9ca328acc 100644 (file)
     root.kee as projectKey
   </sql>
 
-  <sql id="sortFilter">
-    <if test="sort != null">
-      order by
+  <sql id="sortColumn">
+    <if test="sort != null">,
       <choose>
         <when test="'SEVERITY'.equals(sort.name())">
-          i.severity
+          i.severity as severity
         </when>
         <when test="'STATUS'.equals(sort.name())">
-          i.status
+          i.status as status
         </when>
         <when test="'ASSIGNEE'.equals(sort.name())">
-          i.assignee
+          i.assignee as assignee
         </when>
         <when test="'CREATION_DATE'.equals(sort.name())">
-          i.issue_creation_date
+          i.issue_creation_date as issueCreationDate
         </when>
         <when test="'UPDATE_DATE'.equals(sort.name())">
-          i.issue_update_date
+          i.issue_update_date as issueUpdateDate
         </when>
         <when test="'CLOSE_DATE'.equals(sort.name())">
-          i.issue_close_date
+          i.issue_close_date as issueCloseDate
         </when>
       </choose>
-      <choose>
-        <when test="true.equals(asc)">
-          asc
-        </when>
-        <otherwise>
-          desc
-        </otherwise>
-      </choose>
     </if>
   </sql>
 
       and p.id=i.resource_id
       and i.project_id=root.id
     </where>
-    <include refid="sortFilter"/>
   </select>
 
   <select id="selectIssueAndComponentIds" parameterType="map" resultType="Issue">
 
   <select id="selectIssueAndProjectIds" parameterType="map" resultType="Issue">
     select i.id, i.project_id as projectId
+    <include refid="sortColumn"/>
     <include refid="selectQueryConditions"/>
   </select>
 
         and i.issue_creation_date &lt; #{createdBefore}
       </if>
     </where>
-    <if test="sort == null">
-      order by i.id
-    </if>
-    <include refid="sortFilter"/>
+    order by i.id
   </sql>
 
 </mapper>
index 8418243ce7736fdcf0ca949b612638e3296b47a5..73b04ef1796d40220c232b349bfef8f6903db062 100644 (file)
@@ -224,78 +224,6 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(dao.select(query)).hasSize(3);
   }
 
-  @Test
-  public void should_select_sort_by_severity() {
-    setupData("shared", "should_select_returned_sorted_result_by_severity");
-
-    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.SEVERITY).asc(true).build();
-    List<IssueDto> results = newArrayList(dao.select(query));
-    assertThat(results).hasSize(3);
-    assertThat(results.get(0).getSeverity()).isEqualTo("BLOCKER");
-    assertThat(results.get(1).getSeverity()).isEqualTo("MAJOR");
-    assertThat(results.get(2).getSeverity()).isEqualTo("MINOR");
-  }
-
-  @Test
-  public void should_select_sort_by_status() {
-    setupData("shared", "should_select_returned_sorted_result_by_status");
-
-    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.STATUS).asc(true).build();
-    List<IssueDto> results = newArrayList(dao.select(query));
-    assertThat(results).hasSize(3);
-    assertThat(results.get(0).getStatus()).isEqualTo("CLOSED");
-    assertThat(results.get(1).getStatus()).isEqualTo("OPEN");
-    assertThat(results.get(2).getStatus()).isEqualTo("REOPEN");
-  }
-
-  @Test
-  public void should_select_sort_by_assignee() {
-    setupData("shared", "should_select_returned_sorted_result_by_assignee");
-
-    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.ASSIGNEE).asc(true).build();
-    List<IssueDto> results = newArrayList(dao.select(query));
-    assertThat(results).hasSize(3);
-    assertThat(results.get(0).getAssignee()).isEqualTo("arthur");
-    assertThat(results.get(1).getAssignee()).isEqualTo("henry");
-    assertThat(results.get(2).getAssignee()).isEqualTo("perceval");
-  }
-
-  @Test
-  public void should_select_sort_by_creation_date() {
-    setupData("shared", "should_select_returned_sorted_result_by_creation_date");
-
-    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CREATION_DATE).asc(false).build();
-    List<IssueDto> results = newArrayList(dao.select(query));
-    assertThat(results).hasSize(3);
-    assertThat(results.get(0).getId()).isEqualTo(102);
-    assertThat(results.get(1).getId()).isEqualTo(100);
-    assertThat(results.get(2).getId()).isEqualTo(101);
-  }
-
-  @Test
-  public void should_select_sort_by_update_date() {
-    setupData("shared", "should_select_returned_sorted_result_by_update_date");
-
-    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.UPDATE_DATE).asc(false).build();
-    List<IssueDto> results = newArrayList(dao.select(query));
-    assertThat(results).hasSize(3);
-    assertThat(results.get(0).getId()).isEqualTo(102);
-    assertThat(results.get(1).getId()).isEqualTo(100);
-    assertThat(results.get(2).getId()).isEqualTo(101);
-  }
-
-  @Test
-  public void should_select_sort_by_close_date() {
-    setupData("shared", "should_select_returned_sorted_result_by_close_date");
-
-    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CLOSE_DATE).asc(false).build();
-    List<IssueDto> results = newArrayList(dao.select(query));
-    assertThat(results).hasSize(3);
-    assertThat(results.get(0).getId()).isEqualTo(102);
-    assertThat(results.get(1).getId()).isEqualTo(100);
-    assertThat(results.get(2).getId()).isEqualTo(101);
-  }
-
   @Test
   public void should_select_issue_and_component_ids() {
     setupData("shared", "should_select_issue_and_component_ids");
@@ -313,6 +241,10 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     List<IssueDto> results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
     assertThat(results).hasSize(3);
 
+    IssueDto issueDto = results.get(0);
+    assertThat(issueDto.getId()).isNotNull();
+    assertThat(issueDto.getProjectId()).isNotNull();
+
     results = dao.selectIssueAndProjectIds(query, newArrayList(399), 2);
     assertThat(results).hasSize(2);
 
@@ -320,6 +252,35 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(results).isEmpty();
   }
 
+  @Test
+  public void should_select_issue_and_project_ids_with_sort_column() {
+    setupData("shared", "should_select_issue_and_project_ids");
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.ASSIGNEE).build();
+    List<IssueDto> results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
+    assertThat(results.get(0).getAssignee()).isNotNull();
+
+    query = IssueQuery.builder().sort(IssueQuery.Sort.SEVERITY).build();
+    results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
+    assertThat(results.get(0).getSeverity()).isNotNull();
+
+    query = IssueQuery.builder().sort(IssueQuery.Sort.STATUS).build();
+    results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
+    assertThat(results.get(0).getStatus()).isNotNull();
+
+    query = IssueQuery.builder().sort(IssueQuery.Sort.CREATION_DATE).build();
+    results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
+    assertThat(results.get(0).getIssueCreationDate()).isNotNull();
+
+    query = IssueQuery.builder().sort(IssueQuery.Sort.UPDATE_DATE).build();
+    results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
+    assertThat(results.get(0).getIssueUpdateDate()).isNotNull();
+
+    query = IssueQuery.builder().sort(IssueQuery.Sort.CLOSE_DATE).build();
+    results = dao.selectIssueAndProjectIds(query, newArrayList(399), 1000);
+    assertThat(results.get(0).getIssueCloseDate()).isNotNull();
+  }
+
   @Test
   public void should_select_open_issues() {
     setupData("shared", "should_select_open_issues");
@@ -337,10 +298,10 @@ public class IssueDaoTest extends AbstractDaoTestCase {
   public void should_select_by_ids() {
     setupData("shared", "should_select_by_ids");
 
-    List<IssueDto> results = newArrayList(dao.selectByIds(newArrayList(100l, 101l, 102l), IssueQuery.Sort.CREATION_DATE, false));
+    List<IssueDto> results = newArrayList(dao.selectByIds(newArrayList(100l, 101l, 102l)));
     assertThat(results).hasSize(3);
-    assertThat(results.get(0).getId()).isEqualTo(102);
-    assertThat(results.get(1).getId()).isEqualTo(100);
-    assertThat(results.get(2).getId()).isEqualTo(101);
+    assertThat(results.get(0).getId()).isEqualTo(100);
+    assertThat(results.get(1).getId()).isEqualTo(101);
+    assertThat(results.get(2).getId()).isEqualTo(102);
   }
 }
index caea62016a2291a4f19252c16b28d337444e3461..1ce627cc59fdc2d27725b10aa072e429ed26ca40 100644 (file)
@@ -106,12 +106,17 @@ public class DefaultIssueFinder implements IssueFinder {
       // 2. Select the authorized ids of all the issues that match the query
       List<IssueDto> authorizedIssues = issueDao.selectIssueAndProjectIds(query, rootProjectIds, sqlSession);
 
-      // 3. Apply pagination
-      Paging paging = Paging.create(query.pageSize(), query.pageIndex(), authorizedIssues.size());
-      Set<Long> pagedIssueIds = pagedIssueIds(authorizedIssues, paging);
+      // 3. Sort all authorized issues
+      Collection<IssueDto> authorizedSortedIssues = new IssuesFinderSort(authorizedIssues, query).sort();
+
+      // 4. Apply pagination
+      Paging paging = Paging.create(query.pageSize(), query.pageIndex(), authorizedSortedIssues.size());
+      Set<Long> pagedIssueIds = pagedIssueIds(authorizedSortedIssues, paging);
+
+      // 5. Load issues and their related data (rules, components, projects, comments, action plans, ...) and sort then again
+      Collection<IssueDto> pagedIssues = issueDao.selectByIds(pagedIssueIds, sqlSession);
+      Collection<IssueDto> pagedSortedIssues = new IssuesFinderSort(pagedIssues, query).sort();
 
-      // 4. Load issues and their related data (rules, components, comments, action plans, ...)
-      Collection<IssueDto> pagedIssues = issueDao.selectByIds(pagedIssueIds, query.sort(), query.asc(), sqlSession);
       Map<String, DefaultIssue> issuesByKey = newHashMap();
       List<Issue> issues = newArrayList();
       Set<Integer> ruleIds = Sets.newHashSet();
@@ -119,7 +124,7 @@ public class DefaultIssueFinder implements IssueFinder {
       Set<Integer> projectIds = Sets.newHashSet();
       Set<String> actionPlanKeys = Sets.newHashSet();
       Set<String> users = Sets.newHashSet();
-      for (IssueDto dto : pagedIssues) {
+      for (IssueDto dto : pagedSortedIssues) {
         DefaultIssue defaultIssue = dto.toDefaultIssue();
         issuesByKey.put(dto.getKee(), defaultIssue);
         issues.add(defaultIssue);
@@ -144,15 +149,15 @@ public class DefaultIssueFinder implements IssueFinder {
       }
 
       return new DefaultResults(issues,
-        findRules(ruleIds),
-        findComponents(componentIds),
-        findProjects(projectIds),
-        findActionPlans(actionPlanKeys),
-        findUsers(users),
-        paging,
-        false,
-        authorizedIssues.size() != query.maxResults()
-        // TODO
+                                 findRules(ruleIds),
+                                 findComponents(componentIds),
+                                 findProjects(projectIds),
+                                 findActionPlans(actionPlanKeys),
+                                 findUsers(users),
+                                 paging,
+                                 false,
+                                 authorizedIssues.size() != query.maxResults()
+                                 // TODO
 //        authorizedIssues.size() != allIssues.size()
       );
     } finally {
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java b/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java
new file mode 100644 (file)
index 0000000..086365f
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.issue;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Ordering;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.issue.db.IssueDto;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+class IssuesFinderSort {
+
+  private Collection<IssueDto> issues;
+  private IssueQuery query;
+
+  public IssuesFinderSort(Collection<IssueDto> issues, IssueQuery query) {
+    this.issues = issues;
+    this.query = query;
+  }
+
+  public Collection<IssueDto> sort() {
+    if (query.sort() != null) {
+      IssueProcessor issueProcessor;
+      switch (query.sort()) {
+        case ASSIGNEE:
+          issueProcessor = new AssigneeSortIssueProcessor();
+          break;
+        case SEVERITY:
+          issueProcessor = new SeveritySortIssueProcessor();
+          break;
+        case STATUS:
+          issueProcessor = new StatusSortIssueProcessor();
+          break;
+        case CREATION_DATE:
+          issueProcessor = new CreationDateSortIssueProcessor();
+          break;
+        case UPDATE_DATE:
+          issueProcessor = new UpdateDateSortIssueProcessor();
+          break;
+        case CLOSE_DATE:
+          issueProcessor = new CloseDateSortIssueProcessor();
+          break;
+        default:
+          throw new IllegalArgumentException("Cannot sort issues on field : " + query.sort().name());
+      }
+      return issueProcessor.sort(issues, query.asc());
+    }
+    return issues;
+  }
+
+  abstract static class IssueProcessor {
+    abstract Function sortFieldFunction();
+
+    abstract Ordering sortFieldOrdering(boolean ascending);
+
+    final List<IssueDto> sort(Collection<IssueDto> issueDtos, boolean ascending) {
+      Ordering<IssueDto> ordering = sortFieldOrdering(ascending).onResultOf(sortFieldFunction());
+      return ordering.immutableSortedCopy(issueDtos);
+    }
+  }
+
+  abstract static class TextSortIssueProcessor extends IssueProcessor {
+    @Override
+    Function sortFieldFunction() {
+      return new Function<IssueDto, String>() {
+        public String apply(IssueDto issueDto) {
+          return sortField(issueDto);
+        }
+      };
+    }
+
+    abstract String sortField(IssueDto issueDto);
+
+    @Override
+    Ordering sortFieldOrdering(boolean ascending) {
+      Ordering<String> ordering = Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast();
+      if (!ascending) {
+        ordering = ordering.reverse();
+      }
+      return ordering;
+    }
+  }
+
+  static class AssigneeSortIssueProcessor extends TextSortIssueProcessor {
+    @Override
+    String sortField(IssueDto issueDto) {
+      return issueDto.getAssignee();
+    }
+  }
+
+  static class StatusSortIssueProcessor extends TextSortIssueProcessor {
+    @Override
+    String sortField(IssueDto issueDto) {
+      return issueDto.getStatus();
+    }
+  }
+
+  static class SeveritySortIssueProcessor extends IssueProcessor {
+    @Override
+    Function sortFieldFunction() {
+      return new Function<IssueDto, Integer>() {
+        public Integer apply(IssueDto issueDto) {
+          return Severity.ALL.indexOf(issueDto.getSeverity());
+        }
+      };
+    }
+
+    @Override
+    Ordering sortFieldOrdering(boolean ascending) {
+      Ordering<Integer> ordering = Ordering.<Integer>natural().nullsLast();
+      if (!ascending) {
+        ordering = ordering.reverse();
+      }
+      return ordering;
+    }
+  }
+
+  abstract static class DateSortRowProcessor extends IssueProcessor {
+    @Override
+    Function sortFieldFunction() {
+      return new Function<IssueDto, Date>() {
+        public Date apply(IssueDto issueDto) {
+          return sortField(issueDto);
+        }
+      };
+    }
+
+    abstract Date sortField(IssueDto issueDto);
+
+    @Override
+    Ordering sortFieldOrdering(boolean ascending) {
+      Ordering<Date> ordering = Ordering.<Date>natural().nullsLast();
+      if (!ascending) {
+        ordering = ordering.reverse();
+      }
+      return ordering;
+    }
+  }
+
+  static class CreationDateSortIssueProcessor extends DateSortRowProcessor {
+    @Override
+    Date sortField(IssueDto issueDto) {
+      return issueDto.getIssueCreationDate();
+    }
+  }
+
+  static class UpdateDateSortIssueProcessor extends DateSortRowProcessor {
+    @Override
+    Date sortField(IssueDto issueDto) {
+      return issueDto.getIssueUpdateDate();
+    }
+  }
+
+  static class CloseDateSortIssueProcessor extends DateSortRowProcessor {
+    @Override
+    Date sortField(IssueDto issueDto) {
+      return issueDto.getIssueCloseDate();
+    }
+  }
+}
index b2dc2fcfe90b2d3f64aa0fddde2c9440bd0337df..fbb71cc901b2a1728ae12d627bd76cd55d77e119 100644 (file)
@@ -50,7 +50,6 @@ import static org.mockito.Matchers.anyCollection;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.*;
 
 public class DefaultIssueFinderTest {
@@ -82,7 +81,7 @@ public class DefaultIssueFinderTest {
       .setRuleKey_unit_test_only("squid", "AvoidCycle")
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     verify(issueDao).selectIssueAndProjectIds(eq(query), eq(newHashSet(100)), any(SqlSession.class));
@@ -106,7 +105,7 @@ public class DefaultIssueFinderTest {
       .setProjectKey_unit_test_only("struts")
       .setRuleKey_unit_test_only("squid", "AvoidCycle")
       .setStatus("OPEN").setResolution("OPEN");
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(newArrayList(issue1));
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(newArrayList(issue1));
 
     finder.find(query);
     verify(issueDao).selectIssueAndProjectIds(eq(query), eq(Collections.<Integer>emptySet()), any(SqlSession.class));
@@ -134,7 +133,7 @@ public class DefaultIssueFinderTest {
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndProjectIds(eq(query), eq(newHashSet(100)), any(SqlSession.class))).thenReturn(dtoList);
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.paging().offset()).isEqualTo(0);
@@ -142,7 +141,7 @@ public class DefaultIssueFinderTest {
     assertThat(results.paging().pages()).isEqualTo(2);
 
     // Only one result is expected because the limit is 1
-    verify(issueDao).selectByIds(eq(newHashSet(1L)), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class));
+    verify(issueDao).selectByIds(eq(newHashSet(1L)), any(SqlSession.class));
   }
 
   @Test
@@ -178,7 +177,7 @@ public class DefaultIssueFinderTest {
       .setRuleKey_unit_test_only("squid", "AvoidCycle")
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).hasSize(2);
@@ -206,7 +205,7 @@ public class DefaultIssueFinderTest {
       .setRuleKey_unit_test_only("squid", "AvoidCycle")
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).hasSize(2);
@@ -233,7 +232,7 @@ public class DefaultIssueFinderTest {
                         .setRuleKey_unit_test_only("squid", "AvoidCycle")
                         .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).hasSize(2);
@@ -260,7 +259,7 @@ public class DefaultIssueFinderTest {
       .setRuleKey_unit_test_only("squid", "AvoidCycle")
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
     when(actionPlanService.findByKeys(anyCollection())).thenReturn(newArrayList(actionPlan1, actionPlan2));
 
     IssueQueryResult results = finder.find(query);
@@ -275,7 +274,7 @@ public class DefaultIssueFinderTest {
     grantAccessRights();
     IssueQuery query = IssueQuery.builder().build();
     when(issueDao.selectIssueAndProjectIds(eq(query), anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
-    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
+    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).isEmpty();
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java
new file mode 100644 (file)
index 0000000..d7a4589
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.issue;
+
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Test;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.core.issue.db.IssueDto;
+
+import java.util.Date;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssuesFinderSortTest {
+
+  @Test
+  public void should_sort_by_assignee() {
+    IssueDto issue1 = new IssueDto().setId(1L).setAssignee("perceval");
+    IssueDto issue2 = new IssueDto().setId(2L).setAssignee("arthur");
+    IssueDto issue3 = new IssueDto().setId(3L).setAssignee("vincent");
+    IssueDto issue4 = new IssueDto().setId(4L).setAssignee(null);
+    List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3, issue4);
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.ASSIGNEE).asc(true).build();
+    IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query);
+
+    List<IssueDto> result = newArrayList(issuesFinderSort.sort());
+
+    assertThat(result).hasSize(4);
+    assertThat(result.get(0).getAssignee()).isEqualTo("arthur");
+    assertThat(result.get(1).getAssignee()).isEqualTo("perceval");
+    assertThat(result.get(2).getAssignee()).isEqualTo("vincent");
+    assertThat(result.get(3).getAssignee()).isNull();
+  }
+
+  @Test
+  public void should_sort_by_status() {
+    IssueDto issue1 = new IssueDto().setId(1L).setStatus("CLOSED");
+    IssueDto issue2 = new IssueDto().setId(2L).setStatus("REOPENED");
+    IssueDto issue3 = new IssueDto().setId(3L).setStatus("OPEN");
+    List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3);
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.STATUS).asc(false).build();
+    IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query);
+
+    List<IssueDto> result = newArrayList(issuesFinderSort.sort());
+
+    assertThat(result).hasSize(3);
+    assertThat(result.get(0).getStatus()).isEqualTo("REOPENED");
+    assertThat(result.get(1).getStatus()).isEqualTo("OPEN");
+    assertThat(result.get(2).getStatus()).isEqualTo("CLOSED");
+  }
+
+  @Test
+  public void should_sort_by_severity() {
+    IssueDto issue1 = new IssueDto().setId(1L).setSeverity("INFO");
+    IssueDto issue2 = new IssueDto().setId(2L).setSeverity("BLOCKER");
+    IssueDto issue3 = new IssueDto().setId(3L).setSeverity("MAJOR");
+    List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3);
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.SEVERITY).asc(true).build();
+    IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query);
+
+    List<IssueDto> result = newArrayList(issuesFinderSort.sort());
+
+    assertThat(result).hasSize(3);
+    assertThat(result.get(0).getSeverity()).isEqualTo("INFO");
+    assertThat(result.get(1).getSeverity()).isEqualTo("MAJOR");
+    assertThat(result.get(2).getSeverity()).isEqualTo("BLOCKER");
+  }
+
+  @Test
+  public void should_sort_by_creation_date() {
+    Date date = new Date();
+    Date date1 = DateUtils.addDays(date, -3);
+    Date date2 = DateUtils.addDays(date, -2);
+    Date date3 = DateUtils.addDays(date, -1);
+    IssueDto issue1 = new IssueDto().setId(1L).setIssueCreationDate(date1);
+    IssueDto issue2 = new IssueDto().setId(2L).setIssueCreationDate(date3);
+    IssueDto issue3 = new IssueDto().setId(3L).setIssueCreationDate(date2);
+    List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3);
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CREATION_DATE).asc(false).build();
+    IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query);
+
+    List<IssueDto> result = newArrayList(issuesFinderSort.sort());
+
+    assertThat(result).hasSize(3);
+    assertThat(result.get(0).getIssueCreationDate()).isEqualTo(date3);
+    assertThat(result.get(1).getIssueCreationDate()).isEqualTo(date2);
+    assertThat(result.get(2).getIssueCreationDate()).isEqualTo(date1);
+  }
+
+  @Test
+  public void should_sort_by_update_date() {
+    Date date = new Date();
+    Date date1 = DateUtils.addDays(date, -3);
+    Date date2 = DateUtils.addDays(date, -2);
+    Date date3 = DateUtils.addDays(date, -1);
+    IssueDto issue1 = new IssueDto().setId(1L).setIssueUpdateDate(date1);
+    IssueDto issue2 = new IssueDto().setId(2L).setIssueUpdateDate(date3);
+    IssueDto issue3 = new IssueDto().setId(3L).setIssueUpdateDate(date2);
+    List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3);
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.UPDATE_DATE).asc(false).build();
+    IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query);
+
+    List<IssueDto> result = newArrayList(issuesFinderSort.sort());
+
+    assertThat(result).hasSize(3);
+    assertThat(result.get(0).getIssueUpdateDate()).isEqualTo(date3);
+    assertThat(result.get(1).getIssueUpdateDate()).isEqualTo(date2);
+    assertThat(result.get(2).getIssueUpdateDate()).isEqualTo(date1);
+  }
+
+  @Test
+  public void should_sort_by_close_date() {
+    Date date = new Date();
+    Date date1 = DateUtils.addDays(date, -3);
+    Date date2 = DateUtils.addDays(date, -2);
+    Date date3 = DateUtils.addDays(date, -1);
+    IssueDto issue1 = new IssueDto().setId(1L).setIssueCloseDate(date1);
+    IssueDto issue2 = new IssueDto().setId(2L).setIssueCloseDate(date3);
+    IssueDto issue3 = new IssueDto().setId(3L).setIssueCloseDate(date2);
+    List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3);
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CLOSE_DATE).asc(false).build();
+    IssuesFinderSort issuesFinderSort = new IssuesFinderSort(dtoList, query);
+
+    List<IssueDto> result = newArrayList(issuesFinderSort.sort());
+
+    assertThat(result).hasSize(3);
+    assertThat(result.get(0).getIssueCloseDate()).isEqualTo(date3);
+    assertThat(result.get(1).getIssueCloseDate()).isEqualTo(date2);
+    assertThat(result.get(2).getIssueCloseDate()).isEqualTo(date1);
+  }
+
+}