]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2501 a test plan can have multiple test cases with the same name (see TestNG)
authorSimon Brandhof <simon.brandhof@gmail.com>
Tue, 5 Feb 2013 18:07:42 +0000 (19:07 +0100)
committerSimon Brandhof <simon.brandhof@gmail.com>
Tue, 5 Feb 2013 18:10:17 +0000 (19:10 +0100)
17 files changed:
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/web/tests_viewer.html.erb
sonar-application/pom.xml
sonar-core/src/main/java/org/sonar/core/test/DefaultTestCase.java
sonar-core/src/main/java/org/sonar/core/test/DefaultTestPlan.java
sonar-core/src/main/java/org/sonar/core/test/DefaultTestable.java
sonar-core/src/test/java/org/sonar/core/test/DefaultTestCaseTest.java
sonar-core/src/test/java/org/sonar/core/test/DefaultTestPlanTest.java
sonar-core/src/test/java/org/sonar/core/test/DefaultTestableTest.java
sonar-plugin-api/src/main/java/org/sonar/api/test/MutableTestCase.java
sonar-plugin-api/src/main/java/org/sonar/api/test/TestCase.java
sonar-plugin-api/src/main/java/org/sonar/api/test/TestPlan.java
sonar-plugin-api/src/main/java/org/sonar/api/test/Testable.java
sonar-plugin-api/src/main/java/org/sonar/api/test/exception/TestCaseAlreadyExistsException.java [deleted file]
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/tests_controller.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/test_controller.rb

index 122fd22b7cc1527c661204e20c73994386170a8b..47f5aac6a9190ad06086b68330c4181789ec4ba7 100644 (file)
      end
    else
      # New Test API
-     test_plan = controller.java_facade.getTestPlan(@snapshot.id)
+     test_plan = controller.java_facade.testPlan(@snapshot.id)
      if test_plan
        test_plan.testCases().each do |test|
          test_case = {}
          test_case[:name] = test.name()
-         test_case[:status] = test.status()
+         test_case[:status] = test.status().to_s
          test_case[:time] = test.durationInMs()
          has_covered_lines = test.doesCover()
          test_case[:covered_lines] = test.countCoveredLines() if has_covered_lines
-         if test.status() != 'ok'
+         if test.status().to_s != 'ok'
            test_case[:message] = ''
            test_case[:message] = test.message() if test.message()
            test_case[:stack_trace] = html_escape(test.stackTrace())
index 02576867e15f433b24dbbc43fbd3f3634eff9cd2..7c733839da07cca1f029cafb80f4c885ed837099 100644 (file)
             <configuration>
               <rules>
                 <requireFilesSize>
-                  <maxsize>54000000</maxsize>
+                  <maxsize>55000000</maxsize>
                   <minsize>52000000</minsize>
                   <files>
                     <file>${project.build.directory}/sonar-${project.version}.zip</file>
index d7b200e467391bfe7770a686186b5a6201d3d686..7b535ad7c23ebfd8c691efb64a38614f389c8d46 100644 (file)
@@ -50,24 +50,12 @@ public class DefaultTestCase extends BeanVertex implements MutableTestCase {
     return this;
   }
 
-  public String status() {
-    return (String) getProperty("status");
+  public Status status() {
+    return Status.of((String)getProperty("status"));
   }
 
-  public MutableTestCase setStatus(@Nullable String s) {
-    setProperty("status", s);
-    return this;
-  }
-
-  /**
-   * The key is not blank and unique among the test plan.
-   */
-  public String key() {
-    return (String) getProperty("key");
-  }
-
-  public MutableTestCase setKey(String s) {
-    setProperty("key", s);
+  public MutableTestCase setStatus(@Nullable Status s) {
+    setProperty("status", s == null ? null : s.toString());
     return this;
   }
 
index c1947f9b168ba85eb7d7c0f4b41844e4d09c578f..a9c4b69cc02bfc392a6036d2f09bea4ca5c73db9 100644 (file)
  */
 package org.sonar.core.test;
 
+import com.google.common.collect.Lists;
 import com.tinkerpop.blueprints.Direction;
 import com.tinkerpop.blueprints.Vertex;
 import org.sonar.api.component.Component;
-import org.sonar.api.test.exception.TestCaseAlreadyExistsException;
 import org.sonar.api.test.MutableTestCase;
 import org.sonar.api.test.MutableTestPlan;
 import org.sonar.core.component.ComponentVertex;
@@ -31,6 +31,8 @@ import org.sonar.core.graph.GraphUtil;
 
 import javax.annotation.CheckForNull;
 
+import java.util.List;
+
 public class DefaultTestPlan extends BeanVertex implements MutableTestPlan {
   public Component component() {
     Vertex component = GraphUtil.singleAdjacent(element(), Direction.IN, "testplan");
@@ -47,21 +49,19 @@ public class DefaultTestPlan extends BeanVertex implements MutableTestPlan {
   }
 
   @CheckForNull
-  public MutableTestCase testCaseByKey(String key) {
+  public Iterable<MutableTestCase> testCasesByName(String name) {
+    List<MutableTestCase> result = Lists.newArrayList();
     for (MutableTestCase testCase : testCases()) {
-      if (key.equals(testCase.key())) {
-        return testCase;
+      if (name.equals(testCase.name())) {
+        result.add(testCase);
       }
     }
-    return null;
+    return result;
   }
 
-  public MutableTestCase addTestCase(String key) {
-    if (testCaseByKey(key)!=null) {
-      throw new TestCaseAlreadyExistsException(component().key(), key);
-    }
+  public MutableTestCase addTestCase(String name) {
     DefaultTestCase testCase = beanGraph().createAdjacentVertex(this, DefaultTestCase.class, "testcase");
-    testCase.setKey(key);
+    testCase.setName(name);
     return testCase;
   }
 
index 25a3510f1622f8334871c5fdd8f111b9de52b660..a3b4f2598c4a90f7c8d74b7a619a13aa9aabfc7a 100644 (file)
@@ -53,10 +53,10 @@ public class DefaultTestable extends BeanVertex implements MutableTestable {
     return cases.build();
   }
 
-  public TestCase testCaseByKey(final String key) {
+  public TestCase testCaseByName(final String name) {
     return Iterables.find(testCases(), new Predicate<TestCase>() {
       public boolean apply(TestCase input) {
-        return input.key().equals(key);
+        return input.name().equals(name);
       }
     }, null);
   }
@@ -94,7 +94,7 @@ public class DefaultTestable extends BeanVertex implements MutableTestable {
   public Cover coverOfTestCase(final TestCase testCase) {
     return Iterables.find(getEdges(DefaultCover.class, Direction.IN, "covers"), new Predicate<Cover>() {
       public boolean apply(Cover input) {
-        return input.testCase().key().equals(testCase.key());
+        return input.testCase().name().equals(testCase.name());
       }
     }, null);
   }
index 212216dcf5d0c737ced56c27d0c47b018b850f30..8dc9289bc9d6308e5eb2490bcd856a40746ec1fc 100644 (file)
@@ -83,7 +83,7 @@ public class DefaultTestCaseTest {
   }
 
   @Test
-  public void should_return_cover_of_testable(){
+  public void should_return_cover_of_testable() {
     BeanGraph beanGraph = BeanGraph.createInMemory();
 
     ScanGraph graph = ScanGraph.create();
@@ -106,19 +106,17 @@ public class DefaultTestCaseTest {
     BeanGraph beanGraph = BeanGraph.createInMemory();
     DefaultTestCase testCase = beanGraph.createVertex(DefaultTestCase.class);
 
-    testCase.setKey("T1")
-      .setName("Test one")
+    testCase.setName("T1")
       .setDurationInMs(1234L)
       .setMessage("Error msg")
       .setStackTrace("xxx")
-      .setStatus(TestCase.STATUS_FAIL);
+      .setStatus(TestCase.Status.ERROR);
 
-    assertThat(testCase.key()).isEqualTo("T1");
-    assertThat(testCase.name()).isEqualTo("Test one");
+    assertThat(testCase.name()).isEqualTo("T1");
     assertThat(testCase.message()).isEqualTo("Error msg");
     assertThat(testCase.stackTrace()).isEqualTo("xxx");
     assertThat(testCase.durationInMs()).isEqualTo(1234L);
-    assertThat(testCase.status()).isEqualTo(TestCase.STATUS_FAIL);
+    assertThat(testCase.status()).isEqualTo(TestCase.Status.ERROR);
   }
 
   @Test
index 32a3c32157e482f1f198d63f7151cb197d0e5a82..816bdb13d4c1a6460ad9b9f68087d91c67bef8e5 100644 (file)
@@ -23,10 +23,8 @@ import com.google.common.collect.Iterables;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.sonar.api.test.exception.TestCaseAlreadyExistsException;
 import org.sonar.api.test.MutableTestCase;
 import org.sonar.api.test.TestPlan;
-import org.sonar.core.component.ComponentVertex;
 import org.sonar.core.graph.BeanGraph;
 
 import static org.fest.assertions.Assertions.assertThat;
@@ -53,46 +51,46 @@ public class DefaultTestPlanTest {
 
     assertThat(plan.testCases()).hasSize(2);
     MutableTestCase firstTestCase = Iterables.get(plan.testCases(), 0);
-    assertThat(firstTestCase.key()).isEqualTo("T1");
+    assertThat(firstTestCase.name()).isEqualTo("T1");
     assertThat(firstTestCase.testPlan()).isSameAs(plan);
 
     MutableTestCase secondTestCase = Iterables.get(plan.testCases(), 1);
-    assertThat(secondTestCase.key()).isEqualTo("T2");
+    assertThat(secondTestCase.name()).isEqualTo("T2");
     assertThat(secondTestCase.testPlan()).isSameAs(plan);
   }
 
   @Test
-  public void should_find_test_case_by_key() {
+  public void should_find_test_case_by_name() {
     BeanGraph beanGraph = BeanGraph.createInMemory();
 
     DefaultTestPlan plan = beanGraph.createVertex(DefaultTestPlan.class);
     plan.addTestCase("T1");
     plan.addTestCase("T2");
 
-    assertThat(plan.testCaseByKey("T1").key()).isEqualTo("T1");
-    assertThat(plan.testCaseByKey("T3")).isNull();
+    assertThat(plan.testCasesByName("T1")).hasSize(1);
+    assertThat(Iterables.get(plan.testCasesByName("T1"), 0).name()).isEqualTo("T1");
+    assertThat(plan.testCasesByName("T3")).isEmpty();
   }
 
   @Test
-  public void should_set_type() {
+  public void should_find_multiple_test_cases_by_name() {
     BeanGraph beanGraph = BeanGraph.createInMemory();
 
     DefaultTestPlan plan = beanGraph.createVertex(DefaultTestPlan.class);
-    assertThat(plan.type()).isNull();
+    plan.addTestCase("T1");
+    plan.addTestCase("T1");
 
-    plan.setType(TestPlan.TYPE_UNIT);
-    assertThat(plan.type()).isEqualTo(TestPlan.TYPE_UNIT);
+    assertThat(plan.testCasesByName("T1")).hasSize(2);
   }
 
   @Test
-  public void keys_of_test_cases_should_be_unique() {
-    thrown.expect(TestCaseAlreadyExistsException.class);
-
+  public void should_set_type() {
     BeanGraph beanGraph = BeanGraph.createInMemory();
-    ComponentVertex component = beanGraph.createVertex(ComponentVertex.class);
 
-    DefaultTestPlan plan = beanGraph.createAdjacentVertex(component, DefaultTestPlan.class, "testplan");
-    plan.addTestCase("T1");
-    plan.addTestCase("T1");
+    DefaultTestPlan plan = beanGraph.createVertex(DefaultTestPlan.class);
+    assertThat(plan.type()).isNull();
+
+    plan.setType(TestPlan.TYPE_UNIT);
+    assertThat(plan.type()).isEqualTo(TestPlan.TYPE_UNIT);
   }
 }
index 3a921a2508d774853fdb60d00fae4b8082ded33a..cbf107b16178cb6fc2d1d787c70afbfc6358dbca 100644 (file)
@@ -86,9 +86,9 @@ public class DefaultTestableTest {
     MutableTestCase testCase2 = Iterables.get(plan.testCases(), 1);
     testCase2.setCover(testable, Arrays.asList(12, 48, 49));
 
-    assertThat(testable.testCaseByKey("T1")).isEqualTo(testCase1);
-    assertThat(testable.testCaseByKey("T2")).isEqualTo(testCase2);
-    assertThat(testable.testCaseByKey("Unknown")).isNull();
+    assertThat(testable.testCaseByName("T1")).isEqualTo(testCase1);
+    assertThat(testable.testCaseByName("T2")).isEqualTo(testCase2);
+    assertThat(testable.testCaseByName("Unknown")).isNull();
   }
 
   @Test
index ee258383d9f669c284a3414a99194aa16e04edf3..a65b652750ac48021f9f2df801e69ad12ebed009 100644 (file)
@@ -24,12 +24,10 @@ import javax.annotation.Nullable;
 import java.util.List;
 
 public interface MutableTestCase extends TestCase {
-  MutableTestCase setStatus(String s);
+  MutableTestCase setStatus(Status s);
 
   MutableTestCase setDurationInMs(@Nullable Long l);
 
-  MutableTestCase setName(String s);
-
   MutableTestCase setMessage(String s);
 
   MutableTestCase setStackTrace(String s);
index 168b07ad189251eed79babe46cf072b5bb7e2bb5..5b89bbb7fda36523dc21fbdd59dfd269f6005eec 100644 (file)
  */
 package org.sonar.api.test;
 
+import javax.annotation.Nullable;
+
 public interface TestCase {
-  String STATUS_PASS = "pass";
-  String STATUS_FAIL = "fail";
+  enum Status {
+    OK, FAILURE, ERROR, SKIPPED;
+
+    public static Status of(@Nullable String s) {
+      return s == null ? null : valueOf(s.toUpperCase());
+    }
+  }
 
   /**
    * Duration in milliseconds
    */
   Long durationInMs();
 
-  // pass/fail/...
-  String status();
-
-  /**
-   * The key is not null and unique among the test plan.
-   */
-  String key();
+  Status status();
 
   String name();
 
index 1c334e2442b0dc35c162645fb2dbb85d8e1db126..1b1bb914cb962239b616bd4014c9e919699b5286 100644 (file)
@@ -32,5 +32,5 @@ public interface TestPlan<T extends TestCase> extends Perspective {
 
   Iterable<T> testCases();
 
-  T testCaseByKey(String key);
+  Iterable<T> testCasesByName(String name);
 }
index f11a3a5fef8b380a74fb5d132fe2de4e92b75dec..77ec933b52257e6780c99a21ff1c114949851270 100644 (file)
@@ -28,7 +28,7 @@ public interface Testable extends Perspective {
 
   List<TestCase> testCases();
 
-  TestCase testCaseByKey(String key);
+  TestCase testCaseByName(String key);
 
   int countTestCasesOfLine(Integer line);
 
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/test/exception/TestCaseAlreadyExistsException.java b/sonar-plugin-api/src/main/java/org/sonar/api/test/exception/TestCaseAlreadyExistsException.java
deleted file mode 100644 (file)
index ff1453b..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2012 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar 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.
- *
- * Sonar 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 Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.api.test.exception;
-
-public class TestCaseAlreadyExistsException extends TestException {
-  public TestCaseAlreadyExistsException(String componentKey, String testCaseKey) {
-    super(String.format("Test case already exists for %s: %s", componentKey, testCaseKey));
-  }
-}
index 5127fe218a8df25aa3e7ca35871d74a98fffe535..082e08fb34f946083af3c9e274c07a238968d994 100644 (file)
@@ -325,7 +325,7 @@ public final class JRubyFacade {
 
   public void ruleSeverityChanged(int parentProfileId, int activeRuleId, int oldSeverityId, int newSeverityId, String userName) {
     getProfilesManager().ruleSeverityChanged(parentProfileId, activeRuleId, RulePriority.values()[oldSeverityId],
-        RulePriority.values()[newSeverityId], userName);
+      RulePriority.values()[newSeverityId], userName);
   }
 
   public void ruleDeactivated(int parentProfileId, int deactivatedRuleId, String userName) {
@@ -521,10 +521,10 @@ public final class JRubyFacade {
     // notifier is null when creating the administrator in the migration script 011.
     if (notifier != null) {
       notifier.onNewUser(NewUserHandler.Context.builder()
-          .setLogin(fields.get("login"))
-          .setName(fields.get("name"))
-          .setEmail(fields.get("email"))
-          .build());
+        .setLogin(fields.get("login"))
+        .setName(fields.get("name"))
+        .setEmail(fields.get("email"))
+        .build());
     }
   }
 
@@ -544,11 +544,15 @@ public final class JRubyFacade {
     return get(Periods.class).abbreviation(periodIndex);
   }
 
-  public TestPlan getTestPlan(long snapshotId) {
+  public TestPlan testPlan(long snapshotId) {
     return get(SnapshotPerspectives.class).as(MutableTestPlan.class, snapshotId);
   }
 
-  public Testable getTestable(long snapshotId) {
+  public TestPlan testPlan(String componentKey) {
+    return get(SnapshotPerspectives.class).as(MutableTestPlan.class, componentKey);
+  }
+
+  public Testable testable(long snapshotId) {
     return get(SnapshotPerspectives.class).as(MutableTestable.class, snapshotId);
   }
 
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/tests_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/tests_controller.rb
new file mode 100644 (file)
index 0000000..8c2eda4
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2012 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar 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.
+#
+# Sonar 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 Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+class Api::TestsController < Api::ApiController
+
+  # GET /api/tests/plan?resource=<test file id or key>
+  #
+  # Get the details of a given test plan :
+  # - test cases
+  # - resources covered by test cases
+  #
+  # Since v.3.5
+  #
+  # ==== Examples
+  # - get the test plan defined by the file com/mycompany/FooTest.c of my_project : GET /api/tests/plan?resource=my_project:com/mycompany/FooTest.c
+  #
+  def plan
+    require_parameters :resource
+
+    resource=Project.by_key(params[:resource])
+    not_found("Not found: #{params[:resource]}") unless resource
+    access_denied unless has_role?(:user, resource)
+
+    plan = java_facade.testPlan(resource.key)
+    json = {}
+    if plan
+      json[:type] = plan.type
+      json[:test_cases] = plan.testCases.map do |test_case|
+        test_case_json = {:name => test_case.name}
+        test_case_json[:message] = test_case.message if test_case.message
+        test_case_json[:durationInMs] = test_case.durationInMs if test_case.durationInMs
+        test_case_json[:status] = test_case.status.to_s if test_case.status
+        test_case_json[:stackTrace] = test_case.stackTrace if test_case.stackTrace
+        if test_case.doesCover()
+          test_case_json[:covers] = test_case.covers.map do |cover|
+            cover_json = {}
+            resource = cover.testable.component
+            cover_json[:resourceKey] = resource.key
+            cover_json[:resourceName] = resource.name
+            cover_json[:resourceQualifier] = resource.qualifier
+            cover_json[:lines] = cover.lines
+            cover_json
+          end
+        end
+        test_case_json
+      end
+
+      respond_to do |format|
+        format.json { render :json => jsonp(json) }
+        format.xml { render :xml => xml_not_supported }
+        format.text { render :text => text_not_supported }
+      end
+    end
+  end
+end
\ No newline at end of file
index cd18218585d483be0ee1e7fde81da7c34913c9fb..4fb0999f00fdbb911dc4447239b1a8be3ccf0c1a 100644 (file)
@@ -205,7 +205,7 @@ class ResourceController < ApplicationController
       @conditions_by_line = load_distribution("#{it_prefix}conditions_by_line")
       @covered_conditions_by_line = load_distribution("#{it_prefix}covered_conditions_by_line")
 
-      @testable = java_facade.getTestable(@snapshot.id)
+      @testable = java_facade.testable(@snapshot.id)
       @hits_by_line.each_pair do |line_id, hits|
         line = @lines[line_id-1]
         if line
index dfb93c7980861d9b4b4098d020901d4919b35062..13bdc012ec7978d280b89c0b00e043c9ca22e448 100644 (file)
@@ -25,7 +25,7 @@ class TestController < ApplicationController
     snapshot_id = params[:sid].to_i
 
     @test = params[:test].to_s
-    @test_plan = java_facade.getTestPlan(snapshot_id)
+    @test_plan = java_facade.testPlan(snapshot_id)
     @test_case = @test_plan.testCaseByKey(@test)
     render :partial => 'test/testcase_working_view'
   end
@@ -36,7 +36,7 @@ class TestController < ApplicationController
     snapshot_id = params[:sid].to_i
 
     @line = params[:line].to_i
-    @testable = java_facade.getTestable(snapshot_id)
+    @testable = java_facade.testable(snapshot_id)
     @test_case_by_test_plan = {}
     @testable.testCasesOfLine(@line).each do |test_case|
       test_plan = test_case.testPlan