]> source.dussan.org Git - sonarqube.git/commitdiff
Improve code coverage
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 24 Sep 2014 15:30:31 +0000 (17:30 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 25 Sep 2014 08:39:49 +0000 (10:39 +0200)
17 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/CoveragePerTestSensor.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/TestCaseSensor.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/CoveragePerTestSensorTest.java
plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/TestCaseSensorTest.java
sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdapter.java
sonar-batch/src/main/java/org/sonar/batch/scan2/BaseSensorContext.java
sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseValueCoder.java
sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorStorage.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCase.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseBuilder.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCase.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseBuilder.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseBuilderTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseTest.java

index cf0ff9389bf08badaafa17c81d614a91e40fa12f..1e4c8b49d021e26058d644ae0709d4a157eb87d3 100644 (file)
@@ -81,7 +81,9 @@ public class CoveragePerTestSensor implements Sensor {
       while (lines.hasNext()) {
         coveredLines.add(Integer.parseInt(lines.next()));
       }
-      TestCase testCase = context.getTestCase(testFile, testCaseName);
+      TestCase testCase = context.newTestCase()
+        .inTestFile(testFile)
+        .name(testCaseName);
       if (testCase == null) {
         throw new IllegalStateException("No test case with name " + testCaseName + " on file " + testFile);
       }
index 96c7de1b045c8a2acd0826322dd366a39452e714..e9b9ff79dcf404085054302b16cf759ec74dd060 100644 (file)
@@ -77,13 +77,15 @@ public class TestCaseSensor implements Sensor {
       String message = split.next();
       String stack = split.next();
       long duration = Long.parseLong(split.next());
-      context.addTestCase(context.testCaseBuilder(testFile, name)
-        .type(TestCase.Type.valueOf(type))
+      context.newTestCase()
+        .inTestFile(testFile)
+        .name(name)
+        .ofType(TestCase.Type.valueOf(type))
         .status(TestCase.Status.valueOf(status))
         .message(StringUtils.trimToNull(message))
         .stackTrace(StringUtils.trimToNull(stack))
         .durationInMs(duration)
-        .build());
+        .save();
     } catch (Exception e) {
       throw new IllegalStateException("Error processing line " + lineNumber + " of file " + testplanFile.getAbsolutePath(), e);
     }
index 12e43b8ead6ed03a81ef28441e7428654364f9d1..9c9ed6f2f4603f6c8afdc00e9cad463c7af87456 100644 (file)
@@ -24,13 +24,16 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorStorage;
 import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
 import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseBuilder;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
 
 import java.io.File;
 import java.io.IOException;
@@ -81,10 +84,17 @@ public class CoveragePerTestSensorTest {
     fileSystem.add(inputFile);
     fileSystem.add(testFile);
 
-    TestCase test1 = new DefaultTestCaseBuilder(testFile, "test1").durationInMs(10).build();
-    TestCase test2 = new DefaultTestCaseBuilder(testFile, "test2").durationInMs(10).build();
-    when(context.getTestCase(testFile, "test1")).thenReturn(test1);
-    when(context.getTestCase(testFile, "test2")).thenReturn(test2);
+    final SensorStorage sensorStorage = mock(SensorStorage.class);
+
+    when(context.newTestCase()).thenAnswer(new Answer<TestCase>() {
+      @Override
+      public TestCase answer(InvocationOnMock invocation) throws Throwable {
+        return new DefaultTestCase(sensorStorage);
+      }
+    });
+
+    TestCase test1 = new DefaultTestCase(null).inTestFile(testFile).name("test1");
+    TestCase test2 = new DefaultTestCase(null).inTestFile(testFile).name("test2");
 
     sensor.execute(context);
 
index 9ddae18b1ed61b2f65aa79e44fd0e43d3fe7cd08..4efb5c4d5ead25d426a8e09786f605a6f0dd77bc 100644 (file)
@@ -24,13 +24,16 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorStorage;
 import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
 import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseBuilder;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
 
 import java.io.File;
 import java.io.IOException;
@@ -78,14 +81,29 @@ public class TestCaseSensorTest {
       .setType(Type.TEST);
     fileSystem.add(testFile);
 
-    when(context.testCaseBuilder(testFile, "test1")).thenReturn(new DefaultTestCaseBuilder(testFile, "test1"));
-    when(context.testCaseBuilder(testFile, "test2")).thenReturn(new DefaultTestCaseBuilder(testFile, "test2"));
+    final SensorStorage sensorStorage = mock(SensorStorage.class);
+
+    when(context.newTestCase()).thenAnswer(new Answer<TestCase>() {
+      @Override
+      public TestCase answer(InvocationOnMock invocation) throws Throwable {
+        return new DefaultTestCase(sensorStorage);
+      }
+    });
 
     sensor.execute(context);
 
-    verify(context).addTestCase(new DefaultTestCaseBuilder(testFile, "test1").durationInMs(10).build());
-    verify(context).addTestCase(
-      new DefaultTestCaseBuilder(testFile, "test2").type(TestCase.Type.INTEGRATION).status(TestCase.Status.ERROR).message("message").stackTrace("stack").durationInMs(15).build());
+    verify(sensorStorage).store(new DefaultTestCase(null)
+      .inTestFile(testFile)
+      .name("test1")
+      .durationInMs(10));
+    verify(sensorStorage).store(new DefaultTestCase(null)
+      .inTestFile(testFile)
+      .name("test2")
+      .ofType(TestCase.Type.INTEGRATION)
+      .status(TestCase.Status.ERROR)
+      .message("message")
+      .stackTrace("stack")
+      .durationInMs(15));
   }
 
 }
index 3357f1953b36bbc85c4b65971112e8b641ae1562..6c9b212e3f6eb55e24e98d155d3ec73c88341902 100644 (file)
@@ -174,7 +174,7 @@ public class SensorContextAdapter extends BaseSensorContext {
   }
 
   @Override
-  public void addTestCase(TestCase testCase) {
+  public void store(TestCase testCase) {
     File testRes = getTestResource(((DefaultTestCase) testCase).testFile());
     MutableTestPlan testPlan = perspectives.as(MutableTestPlan.class, testRes);
     if (testPlan != null) {
@@ -188,22 +188,6 @@ public class SensorContextAdapter extends BaseSensorContext {
     }
   }
 
-  @Override
-  public TestCase getTestCase(InputFile testFile, String testCaseName) {
-    File testRes = getTestResource(testFile);
-    MutableTestPlan testPlan = perspectives.as(MutableTestPlan.class, testRes);
-    if (testPlan != null) {
-      Iterable<MutableTestCase> testCases = testPlan.testCasesByName(testCaseName);
-      if (testCases.iterator().hasNext()) {
-        MutableTestCase testCase = testCases.iterator().next();
-        return new DefaultTestCase(testFile, testCaseName, testCase.durationInMs(), TestCase.Status.of(testCase.status().name()), testCase.message(),
-          TestCase.Type.valueOf(testCase
-            .type()), testCase.stackTrace());
-      }
-    }
-    return null;
-  }
-
   @Override
   public void saveCoveragePerTest(TestCase testCase, InputFile coveredFile, List<Integer> coveredLines) {
     Preconditions.checkArgument(coveredFile.type() == Type.MAIN, "Should be a main file: " + coveredFile);
index 09ed5a9f133b4500e03d0ed8bce7952e5974b2be..beb62d9725132465c0e75bd9990ccf11f2967fa3 100644 (file)
@@ -36,8 +36,8 @@ import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
-import org.sonar.api.batch.sensor.test.TestCaseBuilder;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseBuilder;
+import org.sonar.api.batch.sensor.test.TestCase;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
 import org.sonar.api.config.Settings;
 import org.sonar.batch.duplication.BlockCache;
 import org.sonar.batch.duplication.DefaultTokenBuilder;
@@ -151,8 +151,8 @@ public abstract class BaseSensorContext implements SensorContext, SensorStorage
   }
 
   @Override
-  public TestCaseBuilder testCaseBuilder(InputFile testFile, String testCaseName) {
-    return new DefaultTestCaseBuilder(testFile, testCaseName);
+  public TestCase newTestCase() {
+    return new DefaultTestCase(this);
   }
 
 }
index e02999d23754f83d4db9368cc09cdd96cf426f83..e8982ab4da7af7d4be4b4eaf0f1be3ce8489d501 100644 (file)
@@ -144,18 +144,13 @@ public class DefaultSensorContext extends BaseSensorContext {
   }
 
   @Override
-  public void addTestCase(TestCase testCase) {
+  public void store(TestCase testCase) {
     if (testCaseCache.contains(((DefaultTestCase) testCase).testFile(), testCase.name())) {
       throw new IllegalArgumentException("There is already a test case with the same name: " + testCase.name());
     }
     testCaseCache.put(((DefaultTestCase) testCase).testFile(), testCase);
   }
 
-  @Override
-  public TestCase getTestCase(InputFile testFile, String testCaseName) {
-    return testCaseCache.get(testFile, testCaseName);
-  }
-
   @Override
   public void saveCoveragePerTest(TestCase testCase, InputFile coveredFile, List<Integer> coveredLines) {
     Preconditions.checkNotNull(testCase);
index 7cb4b3652497e09a7d1ccf074dd39fb9269bd48d..727b14efb8f10486536b8df0d287b8538c5c64c4 100644 (file)
@@ -72,6 +72,13 @@ class DefaultTestCaseValueCoder implements ValueCoder {
     long duration = value.getLong();
     TestCase.Type type = TestCase.Type.values()[value.getInt()];
     TestCase.Status status = TestCase.Status.values()[value.getInt()];
-    return new DefaultTestCase(testFile, name, duration != -1 ? duration : null, status, message, type, stack);
+    return new DefaultTestCase(null)
+      .inTestFile(testFile)
+      .ofType(type)
+      .name(name)
+      .durationInMs(duration != -1 ? duration : null)
+      .status(status)
+      .message(message)
+      .stackTrace(stack);
   }
 }
index 865e3b50f6bf472d6e5cd49fcb469ff4f2ce9f51..f949f7c544d88238ae5f4fa27bab800c3885af7a 100644 (file)
@@ -26,11 +26,14 @@ import org.junit.rules.ExpectedException;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.batch.SensorContext;
 import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.batch.sensor.issue.Issue.Severity;
 import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.component.ResourcePerspectives;
@@ -39,8 +42,11 @@ import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.MetricFinder;
+import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.resources.Directory;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.batch.duplication.BlockCache;
 import org.sonar.batch.duplication.DuplicationCache;
@@ -62,6 +68,7 @@ public class SensorContextAdapterTest {
   private SensorContext sensorContext;
   private Settings settings;
   private ResourcePerspectives resourcePerspectives;
+  private Project project;
 
   @Before
   public void prepare() {
@@ -69,12 +76,14 @@ public class SensorContextAdapterTest {
     fs = new DefaultFileSystem();
     MetricFinder metricFinder = mock(MetricFinder.class);
     when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
+    when(metricFinder.findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
     sensorContext = mock(SensorContext.class);
     settings = new Settings();
     resourcePerspectives = mock(ResourcePerspectives.class);
     ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
     BlockCache blockCache = mock(BlockCache.class);
-    adaptor = new SensorContextAdapter(sensorContext, metricFinder, new Project("myProject"),
+    project = new Project("myProject");
+    adaptor = new SensorContextAdapter(sensorContext, metricFinder, project,
       resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class), mock(SonarIndex.class));
   }
 
@@ -89,7 +98,20 @@ public class SensorContextAdapterTest {
   }
 
   @Test
-  public void shouldAddMeasureToSensorContext() {
+  public void shouldFailIfUnknowMetric() {
+    InputFile file = new DefaultInputFile("foo", "src/Foo.php");
+
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("Unknow metric with key: lines");
+
+    adaptor.store(new DefaultMeasure()
+      .onFile(file)
+      .forMetric(CoreMetrics.LINES)
+      .withValue(10));
+  }
+
+  @Test
+  public void shouldSaveFileMeasureToSensorContext() {
     InputFile file = new DefaultInputFile("foo", "src/Foo.php");
 
     ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
@@ -106,7 +128,44 @@ public class SensorContextAdapterTest {
   }
 
   @Test
-  public void shouldAddIssue() {
+  public void shouldSetAppropriatePersistenceMode() {
+    // Metric FUNCTION_COMPLEXITY_DISTRIBUTION is only persisted on directories.
+
+    InputFile file = new DefaultInputFile("foo", "src/Foo.php");
+
+    ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
+    when(sensorContext.saveMeasure(eq(file), argumentCaptor.capture())).thenReturn(null);
+
+    adaptor.store(new DefaultMeasure()
+      .onFile(file)
+      .forMetric(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION)
+      .withValue("foo"));
+
+    org.sonar.api.measures.Measure m = argumentCaptor.getValue();
+    assertThat(m.getData()).isEqualTo("foo");
+    assertThat(m.getMetric()).isEqualTo(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
+    assertThat(m.getPersistenceMode()).isEqualTo(PersistenceMode.MEMORY);
+
+  }
+
+  @Test
+  public void shouldSaveProjectMeasureToSensorContext() {
+
+    ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class);
+    when(sensorContext.saveMeasure(argumentCaptor.capture())).thenReturn(null);
+
+    adaptor.store(new DefaultMeasure()
+      .onProject()
+      .forMetric(CoreMetrics.NCLOC)
+      .withValue(10));
+
+    org.sonar.api.measures.Measure m = argumentCaptor.getValue();
+    assertThat(m.getValue()).isEqualTo(10.0);
+    assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC);
+  }
+
+  @Test
+  public void shouldAddIssueOnFile() {
     InputFile file = new DefaultInputFile("foo", "src/Foo.php");
 
     ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
@@ -127,6 +186,57 @@ public class SensorContextAdapterTest {
     assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
     assertThat(issue.message()).isEqualTo("Foo");
     assertThat(issue.line()).isEqualTo(3);
+    assertThat(issue.severity()).isNull();
+    assertThat(issue.effortToFix()).isEqualTo(10.0);
+  }
+
+  @Test
+  public void shouldAddIssueOnDirectory() {
+    InputDir dir = new DefaultInputDir("foo", "src");
+
+    ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
+
+    Issuable issuable = mock(Issuable.class);
+    when(resourcePerspectives.as(Issuable.class, Directory.create("src"))).thenReturn(issuable);
+
+    when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true);
+
+    adaptor.store(new DefaultIssue()
+      .onDir(dir)
+      .ruleKey(RuleKey.of("foo", "bar"))
+      .message("Foo")
+      .effortToFix(10.0));
+
+    Issue issue = argumentCaptor.getValue();
+    assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
+    assertThat(issue.message()).isEqualTo("Foo");
+    assertThat(issue.line()).isNull();
+    assertThat(issue.severity()).isNull();
     assertThat(issue.effortToFix()).isEqualTo(10.0);
   }
+
+  @Test
+  public void shouldAddIssueOnProject() {
+    ArgumentCaptor<Issue> argumentCaptor = ArgumentCaptor.forClass(Issue.class);
+
+    Issuable issuable = mock(Issuable.class);
+    when(resourcePerspectives.as(Issuable.class, (Resource) project)).thenReturn(issuable);
+
+    when(issuable.addIssue(argumentCaptor.capture())).thenReturn(true);
+
+    adaptor.store(new DefaultIssue()
+      .onProject()
+      .ruleKey(RuleKey.of("foo", "bar"))
+      .message("Foo")
+      .overrideSeverity(Severity.BLOCKER)
+      .effortToFix(10.0));
+
+    Issue issue = argumentCaptor.getValue();
+    assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("foo", "bar"));
+    assertThat(issue.message()).isEqualTo("Foo");
+    assertThat(issue.line()).isNull();
+    assertThat(issue.severity()).isEqualTo("BLOCKER");
+    assertThat(issue.effortToFix()).isEqualTo(10.0);
+  }
+
 }
index b79471f7975904a9babc8c62211bd0fe3fb7cca5..76cab2f47900d8a950b0e085cff391cfaa3899bf 100644 (file)
@@ -30,11 +30,8 @@ import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
 import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.TestCaseBuilder;
 import org.sonar.api.config.Settings;
 
-import javax.annotation.CheckForNull;
-
 import java.io.Serializable;
 import java.util.List;
 
@@ -115,26 +112,10 @@ public interface SensorContext {
 
   /**
    * Create a new test case for the given test file.
-   * @param testFile An {@link InputFile} with type {@link InputFile.Type#TEST}
-   * @param testCaseName name of the test case
-   * @since 5.0
-   */
-  TestCaseBuilder testCaseBuilder(InputFile testFile, String testCaseName);
-
-  /**
-   * Add a new test case.
-   * Use {@link #testCaseBuilder(InputFile, String)} to create a new {@link TestCase}
-   * @throws IllegalArgumentException if a test case with same name was already added on the same file.
-   * @since 5.0
-   */
-  void addTestCase(TestCase testCase);
-
-  /**
-   * Get a {@link TestCase} that has been previously added to the context with {@link #addTestCase(TestCase)}.
+   * Don't forget to call {@link TestCase#save()} once all parameters are provided.
    * @since 5.0
    */
-  @CheckForNull
-  TestCase getTestCase(InputFile testFile, String testCaseName);
+  TestCase newTestCase();
 
   /**
    * Register coverage of a given test case on another main file. TestCase should have been registered using {@link #testPlanBuilder(InputFile)}
index ce2bbc1dbd3da000cc53fa014aee8dd1b6c21ce9..8298250523b95ef247866ffc9677d24d836699ff 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.api.batch.sensor;
 
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
+import org.sonar.api.batch.sensor.test.TestCase;
 
 /**
  * Interface for storing data computed by sensors.
@@ -32,4 +33,6 @@ public interface SensorStorage {
 
   void store(Issue issue);
 
+  void store(TestCase testCase);
+
 }
index a4e95e62e40e9b1a2caa7671e14b4b8b6696a030..37b1047bec293ba35120739e644a75e7ff854f38 100644 (file)
  */
 package org.sonar.api.batch.sensor.test;
 
+import org.sonar.api.batch.fs.InputFile;
+
 import javax.annotation.Nullable;
 
 /**
- * Represents a single test in a test plan.
+ * Represents a single test in a test file.
  * @since 5.0
- *
  */
 public interface TestCase {
+
+  /**
+   * Test execution status.
+   */
   enum Status {
     OK, FAILURE, ERROR, SKIPPED;
 
@@ -35,16 +40,42 @@ public interface TestCase {
     }
   }
 
+  /**
+   * Test type.
+   */
   enum Type {
     UNIT, INTEGRATION;
   }
 
+  /**
+   * InputFile where this test is located.
+   */
+  InputFile testFile();
+
+  /**
+   * Set file where this test is located. Mandatory.
+   */
+  TestCase inTestFile(InputFile testFile);
+
   /**
    * Duration in milliseconds
    */
   Long durationInMs();
 
-  Type type();
+  /**
+   * Duration in milliseconds
+   */
+  TestCase durationInMs(long duration);
+
+  /**
+   * Name of this test case.
+   */
+  String name();
+
+  /**
+   * Set name of this test. Name is mandatory.
+   */
+  TestCase name(String name);
 
   /**
    * Status of execution of the test.
@@ -52,18 +83,43 @@ public interface TestCase {
   Status status();
 
   /**
-   * Name of this test case.
+   * Status of execution of the test.
    */
-  String name();
+  TestCase status(Status status);
 
   /**
    * Message (usually in case of {@link Status#ERROR} or {@link Status#FAILURE}).
    */
   String message();
 
+  /**
+   * Message (usually in case of {@link Status#ERROR} or {@link Status#FAILURE}).
+   */
+  TestCase message(String message);
+
+  /**
+   * Type of test.
+   */
+  Type type();
+
+  /**
+   * Type of test.
+   */
+  TestCase ofType(Type type);
+
   /**
    * Stacktrace (usually in case of {@link Status#ERROR}).
    */
   String stackTrace();
 
+  /**
+   * Set stacktrace (usually in case of {@link Status#ERROR}).
+   */
+  TestCase stackTrace(String stackTrace);
+
+  /**
+   * Call this method only once when your are done with defining the test case.
+   */
+  void save();
+
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseBuilder.java
deleted file mode 100644 (file)
index 9667ff7..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.api.batch.sensor.test;
-
-import org.sonar.api.batch.sensor.test.TestCase.Status;
-import org.sonar.api.batch.sensor.test.TestCase.Type;
-
-/**
- * Builder to create a new TestCase on a test file.
- * @since 5.0
- */
-public interface TestCaseBuilder {
-
-  /**
-   * Duration in milliseconds
-   */
-  TestCaseBuilder durationInMs(long duration);
-
-  /**
-   * Status of execution of the test.
-   */
-  TestCaseBuilder status(Status status);
-
-  /**
-   * Message (usually in case of {@link Status#ERROR} or {@link Status#FAILURE}).
-   */
-  TestCaseBuilder message(String message);
-
-  /**
-   * Type of test.
-   */
-  TestCaseBuilder type(Type type);
-
-  /**
-   * Stacktrace (usually in case of {@link Status#ERROR}).
-   */
-  TestCaseBuilder stackTrace(String stackTrace);
-
-  /**
-   * Call this method only once when your are done with defining the test case.
-   */
-  TestCase build();
-
-}
index 5248a9d05a133072a112c23ba37e9605b504957a..7d05c23f25940c523d90d69f4bc94820f3aefba3 100644 (file)
  */
 package org.sonar.api.batch.sensor.test.internal;
 
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.SensorStorage;
 import org.sonar.api.batch.sensor.test.TestCase;
 
 import javax.annotation.CheckForNull;
@@ -31,22 +34,65 @@ import javax.annotation.Nullable;
 
 public class DefaultTestCase implements TestCase {
 
-  private final InputFile testFile;
-  private final String name;
-  private final Long duration;
-  private final Status status;
-  private final String message;
-  private final Type type;
-  private final String stackTrace;
+  private final SensorStorage storage;
+  private InputFile testFile;
+  private String name;
+  private Long duration;
+  private TestCase.Status status = Status.OK;
+  private String message;
+  private TestCase.Type type = Type.UNIT;
+  private String stackTrace;
 
-  public DefaultTestCase(InputFile testFile, String name, @Nullable Long duration, Status status, @Nullable String message, Type type, @Nullable String stackTrace) {
+  public DefaultTestCase(SensorStorage storage) {
+    this.storage = storage;
+  }
+
+  @Override
+  public DefaultTestCase inTestFile(InputFile testFile) {
+    Preconditions.checkNotNull(testFile, "TestFile cannot be null");
+    Preconditions.checkArgument(testFile.type() == InputFile.Type.TEST, "Should be a test file: " + testFile);
     this.testFile = testFile;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCase name(String name) {
+    Preconditions.checkArgument(StringUtils.isNotBlank(name), "Test name is mandatory and should not be blank");
     this.name = name;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCase durationInMs(long duration) {
+    Preconditions.checkArgument(duration >= 0, "Test duration must be positive (got: " + duration + ")");
     this.duration = duration;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCase status(TestCase.Status status) {
+    Preconditions.checkNotNull(status);
     this.status = status;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCase message(@Nullable String message) {
     this.message = message;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCase ofType(TestCase.Type type) {
+    Preconditions.checkNotNull(type);
     this.type = type;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCase stackTrace(@Nullable String stackTrace) {
     this.stackTrace = stackTrace;
+    return this;
   }
 
   public InputFile testFile() {
@@ -86,6 +132,14 @@ public class DefaultTestCase implements TestCase {
     return stackTrace;
   }
 
+  @Override
+  public void save() {
+    Preconditions.checkNotNull(this.storage, "No persister on this object");
+    Preconditions.checkNotNull(testFile, "TestFile is mandatory");
+    Preconditions.checkNotNull(name, "TestFile is mandatory");
+    storage.store(this);
+  }
+
   // Just for unit tests
   @Override
   public boolean equals(Object obj) {
@@ -135,4 +189,5 @@ public class DefaultTestCase implements TestCase {
       .append("stackTrace", stackTrace)
       .toString();
   }
+
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseBuilder.java
deleted file mode 100644 (file)
index 0b6e34e..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.api.batch.sensor.test.internal;
-
-import com.google.common.base.Preconditions;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.TestCase.Status;
-import org.sonar.api.batch.sensor.test.TestCase.Type;
-import org.sonar.api.batch.sensor.test.TestCaseBuilder;
-
-import javax.annotation.Nullable;
-
-public class DefaultTestCaseBuilder implements TestCaseBuilder {
-
-  private final InputFile testFile;
-  private final String name;
-  private Long duration;
-  private TestCase.Status status = Status.OK;
-  private String message;
-  private TestCase.Type type = Type.UNIT;
-  private String stackTrace;
-
-  public DefaultTestCaseBuilder(InputFile testFile, String name) {
-    Preconditions.checkArgument(testFile.type() == InputFile.Type.TEST, "Should be a test file: " + testFile);
-    Preconditions.checkArgument(StringUtils.isNotBlank(name), "Test name should not be blank");
-    this.testFile = testFile;
-    this.name = name;
-  }
-
-  @Override
-  public TestCaseBuilder durationInMs(long duration) {
-    Preconditions.checkArgument(duration >= 0, "Test duration must be positive (got: " + duration + ")");
-    this.duration = duration;
-    return this;
-  }
-
-  @Override
-  public TestCaseBuilder status(TestCase.Status status) {
-    Preconditions.checkNotNull(status);
-    this.status = status;
-    return this;
-  }
-
-  @Override
-  public TestCaseBuilder message(@Nullable String message) {
-    this.message = message;
-    return this;
-  }
-
-  @Override
-  public TestCaseBuilder type(TestCase.Type type) {
-    this.type = type;
-    return this;
-  }
-
-  @Override
-  public TestCaseBuilder stackTrace(@Nullable String stackTrace) {
-    this.stackTrace = stackTrace;
-    return this;
-  }
-
-  @Override
-  public TestCase build() {
-    return new DefaultTestCase(this.testFile, this.name, this.duration, this.status, this.message, this.type, this.stackTrace);
-  }
-
-}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseBuilderTest.java
deleted file mode 100644 (file)
index 27a9777..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.api.batch.sensor.test.internal;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.test.TestCase.Status;
-import org.sonar.api.batch.sensor.test.TestCase.Type;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class DefaultTestCaseBuilderTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  private InputFile parent = new DefaultInputFile("foo", "src/Foo.php").setType(InputFile.Type.TEST);
-
-  @Test
-  public void testBuilder() throws Exception {
-    DefaultTestCaseBuilder builder = new DefaultTestCaseBuilder(parent, "myTest");
-    assertThat(builder.durationInMs(1)
-      .message("message")
-      .stackTrace("stack")
-      .status(Status.ERROR)
-      .type(Type.UNIT)
-      .build()).isEqualTo(new DefaultTestCase(parent, "myTest", 1L, Status.ERROR, "message", Type.UNIT, "stack"));
-  }
-
-  @Test
-  public void testBuilderWithDefaultValues() throws Exception {
-    DefaultTestCaseBuilder builder = new DefaultTestCaseBuilder(parent, "myTest");
-    assertThat(builder.build()).isEqualTo(
-      new DefaultTestCase(parent, "myTest", null, Status.OK, null, Type.UNIT, null));
-  }
-
-  @Test
-  public void testInvalidDuration() throws Exception {
-    DefaultTestCaseBuilder builder = new DefaultTestCaseBuilder(parent, "myTest");
-
-    thrown.expect(IllegalArgumentException.class);
-
-    builder.durationInMs(-3)
-      .message("message")
-      .stackTrace("stack")
-      .status(Status.ERROR)
-      .type(Type.UNIT)
-      .build();
-  }
-
-}
index ec5668ac3cad55b8063492a2884355a2369160d8..a987eb155d4e090cc7f6ae976bd0f243317d20cd 100644 (file)
@@ -19,7 +19,9 @@
  */
 package org.sonar.api.batch.sensor.test.internal;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.test.TestCase.Status;
@@ -29,25 +31,83 @@ import static org.fest.assertions.Assertions.assertThat;
 
 public class DefaultTestCaseTest {
 
-  private InputFile parent = new DefaultInputFile("foo", "src/Foo.php");
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  private InputFile parent = new DefaultInputFile("foo", "src/Foo.php").setType(InputFile.Type.TEST);
+
+  @Test
+  public void testCreation() throws Exception {
+    DefaultTestCase testCase = new DefaultTestCase(null)
+      .inTestFile(parent)
+      .name("myTest")
+      .durationInMs(1)
+      .message("message")
+      .stackTrace("stack")
+      .status(Status.ERROR)
+      .ofType(Type.UNIT);
+
+    assertThat(testCase.name()).isEqualTo("myTest");
+    assertThat(testCase.testFile()).isEqualTo(parent);
+    assertThat(testCase.durationInMs()).isEqualTo(1L);
+    assertThat(testCase.message()).isEqualTo("message");
+    assertThat(testCase.stackTrace()).isEqualTo("stack");
+    assertThat(testCase.status()).isEqualTo(Status.ERROR);
+    assertThat(testCase.type()).isEqualTo(Type.UNIT);
+  }
 
   @Test
-  public void testDefaultTestCaseTest() {
-    DefaultTestCase testCase1 = new DefaultTestCase(parent, "myTest", 1L, Status.ERROR, "message", Type.UNIT, "stack");
-
-    assertThat(testCase1.name()).isEqualTo("myTest");
-    assertThat(testCase1.durationInMs()).isEqualTo(1L);
-    assertThat(testCase1.status()).isEqualTo(Status.ERROR);
-    assertThat(testCase1.message()).isEqualTo("message");
-    assertThat(testCase1.type()).isEqualTo(Type.UNIT);
-    assertThat(testCase1.stackTrace()).isEqualTo("stack");
+  public void testCreationWithDefaultValues() throws Exception {
+    DefaultTestCase testCase = new DefaultTestCase(null)
+      .inTestFile(parent)
+      .name("myTest");
+
+    assertThat(testCase.name()).isEqualTo("myTest");
+    assertThat(testCase.testFile()).isEqualTo(parent);
+    assertThat(testCase.durationInMs()).isNull();
+    assertThat(testCase.message()).isNull();
+    assertThat(testCase.stackTrace()).isNull();
+    assertThat(testCase.status()).isEqualTo(Status.OK);
+    assertThat(testCase.type()).isEqualTo(Type.UNIT);
+  }
+
+  @Test
+  public void testInvalidDuration() throws Exception {
+    DefaultTestCase builder = new DefaultTestCase(null)
+      .inTestFile(parent)
+      .name("myTest");
+
+    thrown.expect(IllegalArgumentException.class);
+
+    builder.durationInMs(-3);
   }
 
   @Test
   public void testEqualsHashCodeToString() {
-    DefaultTestCase testCase1 = new DefaultTestCase(parent, "myTest", 1L, Status.ERROR, "message", Type.UNIT, "stack");
-    DefaultTestCase testCase1a = new DefaultTestCase(parent, "myTest", 1L, Status.ERROR, "message", Type.UNIT, "stack");
-    DefaultTestCase testCase2 = new DefaultTestCase(new DefaultInputFile("foo2", "src/Foo.php"), "myTest2", 2L, Status.FAILURE, "message2", Type.INTEGRATION, null);
+    DefaultTestCase testCase1 = new DefaultTestCase(null)
+      .inTestFile(parent)
+      .name("myTest")
+      .durationInMs(1)
+      .message("message")
+      .stackTrace("stack")
+      .status(Status.ERROR)
+      .ofType(Type.UNIT);
+    DefaultTestCase testCase1a = new DefaultTestCase(null)
+      .inTestFile(parent)
+      .name("myTest")
+      .durationInMs(1)
+      .message("message")
+      .stackTrace("stack")
+      .status(Status.ERROR)
+      .ofType(Type.UNIT);
+    DefaultTestCase testCase2 = new DefaultTestCase(null)
+      .inTestFile(new DefaultInputFile("foo2", "src/Foo.php").setType(InputFile.Type.TEST))
+      .name("myTest2")
+      .durationInMs(2)
+      .message("message2")
+      .stackTrace("null")
+      .status(Status.FAILURE)
+      .ofType(Type.INTEGRATION);
 
     assertThat(testCase1).isEqualTo(testCase1);
     assertThat(testCase1).isEqualTo(testCase1a);