]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5389 Refactor test coverage API
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 13 Oct 2014 13:08:30 +0000 (15:08 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 14 Oct 2014 12:26:46 +0000 (14:26 +0200)
32 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/mediumtest/BatchMediumTester.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/scan2/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/test/CoveragePerTestCache.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseCoverageValueCoder.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseExecutionValueCoder.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseValueCoder.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/test/TestCaseCache.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/test/TestCaseCoverageCache.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/test/TestCaseExecutionCache.java [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore [new file with mode: 0644]
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/dependency/internal/DefaultDependency.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultStorable.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCase.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseCoverage.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseExecution.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCase.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseCoverage.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseExecution.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/test/TestCase.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseExecutionTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseTest.java [deleted file]

index 1e4c8b49d021e26058d644ae0709d4a157eb87d3..70fe9b78ff77f5a266bc4e4dcbc2920180e0b79e 100644 (file)
@@ -30,7 +30,6 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.sensor.Sensor;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.api.batch.sensor.test.TestCase;
 import org.sonar.xoo.Xoo;
 
 import java.io.File;
@@ -81,13 +80,12 @@ public class CoveragePerTestSensor implements Sensor {
       while (lines.hasNext()) {
         coveredLines.add(Integer.parseInt(lines.next()));
       }
-      TestCase testCase = context.newTestCase()
-        .inTestFile(testFile)
-        .name(testCaseName);
-      if (testCase == null) {
-        throw new IllegalStateException("No test case with name " + testCaseName + " on file " + testFile);
-      }
-      context.saveCoveragePerTest(testCase, mainFile, coveredLines);
+      context.newTestCaseCoverage()
+        .testFile(testFile)
+        .testName(testCaseName)
+        .cover(mainFile)
+        .onLines(coveredLines)
+        .save();
     } catch (Exception e) {
       throw new IllegalStateException("Error processing line " + lineNumber + " of file " + coverPerTest.getAbsolutePath(), e);
     }
index e9b9ff79dcf404085054302b16cf759ec74dd060..12f231c17e8c1dce90c6d4b0470b4acd35151804 100644 (file)
@@ -30,7 +30,7 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.sensor.Sensor;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.api.batch.sensor.test.TestCase;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
 import org.sonar.xoo.Xoo;
 
 import java.io.File;
@@ -77,11 +77,11 @@ public class TestCaseSensor implements Sensor {
       String message = split.next();
       String stack = split.next();
       long duration = Long.parseLong(split.next());
-      context.newTestCase()
+      context.newTestCaseExecution()
         .inTestFile(testFile)
         .name(name)
-        .ofType(TestCase.Type.valueOf(type))
-        .status(TestCase.Status.valueOf(status))
+        .ofType(TestCaseExecution.Type.valueOf(type))
+        .status(TestCaseExecution.Status.valueOf(status))
         .message(StringUtils.trimToNull(message))
         .stackTrace(StringUtils.trimToNull(stack))
         .durationInMs(duration)
index 9c9ed6f2f4603f6c8afdc00e9cad463c7af87456..e22b81eb5f8965e56c2534c13114f5d51a8d4347 100644 (file)
@@ -32,8 +32,8 @@ 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.DefaultTestCase;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseCoverage;
 
 import java.io.File;
 import java.io.IOException;
@@ -86,19 +86,24 @@ public class CoveragePerTestSensorTest {
 
     final SensorStorage sensorStorage = mock(SensorStorage.class);
 
-    when(context.newTestCase()).thenAnswer(new Answer<TestCase>() {
+    when(context.newTestCaseCoverage()).thenAnswer(new Answer<TestCaseCoverage>() {
       @Override
-      public TestCase answer(InvocationOnMock invocation) throws Throwable {
-        return new DefaultTestCase(sensorStorage);
+      public TestCaseCoverage answer(InvocationOnMock invocation) throws Throwable {
+        return new DefaultTestCaseCoverage(sensorStorage);
       }
     });
 
-    TestCase test1 = new DefaultTestCase(null).inTestFile(testFile).name("test1");
-    TestCase test2 = new DefaultTestCase(null).inTestFile(testFile).name("test2");
-
     sensor.execute(context);
 
-    verify(context).saveCoveragePerTest(test1, inputFile, Arrays.asList(1, 2, 3, 4));
-    verify(context).saveCoveragePerTest(test2, inputFile, Arrays.asList(5, 6, 7));
+    verify(sensorStorage).store(new DefaultTestCaseCoverage()
+      .testFile(testFile)
+      .testName("test1")
+      .cover(inputFile)
+      .onLines(Arrays.asList(1, 2, 3, 4)));
+    verify(sensorStorage).store(new DefaultTestCaseCoverage()
+      .testFile(testFile)
+      .testName("test2")
+      .cover(inputFile)
+      .onLines(Arrays.asList(5, 6, 7)));
   }
 }
index 4efb5c4d5ead25d426a8e09786f605a6f0dd77bc..49b669707e0bed2bbbf108449be0b400c36f48b5 100644 (file)
@@ -32,8 +32,8 @@ 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.DefaultTestCase;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
 
 import java.io.File;
 import java.io.IOException;
@@ -83,24 +83,24 @@ public class TestCaseSensorTest {
 
     final SensorStorage sensorStorage = mock(SensorStorage.class);
 
-    when(context.newTestCase()).thenAnswer(new Answer<TestCase>() {
+    when(context.newTestCaseExecution()).thenAnswer(new Answer<TestCaseExecution>() {
       @Override
-      public TestCase answer(InvocationOnMock invocation) throws Throwable {
-        return new DefaultTestCase(sensorStorage);
+      public TestCaseExecution answer(InvocationOnMock invocation) throws Throwable {
+        return new DefaultTestCaseExecution(sensorStorage);
       }
     });
 
     sensor.execute(context);
 
-    verify(sensorStorage).store(new DefaultTestCase(null)
+    verify(sensorStorage).store(new DefaultTestCaseExecution(null)
       .inTestFile(testFile)
       .name("test1")
       .durationInMs(10));
-    verify(sensorStorage).store(new DefaultTestCase(null)
+    verify(sensorStorage).store(new DefaultTestCaseExecution(null)
       .inTestFile(testFile)
       .name("test2")
-      .ofType(TestCase.Type.INTEGRATION)
-      .status(TestCase.Status.ERROR)
+      .ofType(TestCaseExecution.Type.INTEGRATION)
+      .status(TestCaseExecution.Status.ERROR)
       .message("message")
       .stackTrace("stack")
       .durationInMs(15));
index 596edcdec5f52dbe9a598c8864a22dec1a61d7cc..ab8ff21c33c95352827f40c322a3159d8650984b 100644 (file)
@@ -36,7 +36,8 @@ import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.batch.sensor.symbol.Symbol;
-import org.sonar.api.batch.sensor.test.TestCase;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.platform.PluginMetadata;
@@ -61,8 +62,8 @@ import org.sonar.batch.scan2.MeasureCache;
 import org.sonar.batch.scan2.ProjectScanContainer;
 import org.sonar.batch.scan2.ScanTaskObserver;
 import org.sonar.batch.symbol.SymbolData;
-import org.sonar.batch.test.CoveragePerTestCache;
-import org.sonar.batch.test.TestCaseCache;
+import org.sonar.batch.test.TestCaseCoverageCache;
+import org.sonar.batch.test.TestCaseExecutionCache;
 import org.sonar.core.plugins.DefaultPluginMetadata;
 import org.sonar.core.plugins.RemotePlugin;
 import org.sonar.core.source.SnapshotDataTypes;
@@ -233,7 +234,7 @@ public class BatchMediumTester {
     private Map<String, InputDir> inputDirs = new HashMap<String, InputDir>();
     private Map<InputFile, SyntaxHighlightingData> highlightingPerFile = new HashMap<InputFile, SyntaxHighlightingData>();
     private Map<InputFile, SymbolData> symbolTablePerFile = new HashMap<InputFile, SymbolData>();
-    private Map<String, Map<String, TestCase>> testCasesPerFile = new HashMap<String, Map<String, TestCase>>();
+    private Map<String, Map<String, TestCaseExecution>> testCasesPerFile = new HashMap<String, Map<String, TestCaseExecution>>();
     private Map<String, Map<String, Map<String, List<Integer>>>> coveragePerTest = new HashMap<String, Map<String, Map<String, List<Integer>>>>();
     private Map<String, Map<String, Integer>> dependencies = new HashMap<String, Map<String, Integer>>();
 
@@ -258,8 +259,8 @@ public class BatchMediumTester {
     }
 
     private void storeCoveragePerTest(ProjectScanContainer container) {
-      CoveragePerTestCache coveragePerTestCache = container.getComponentByType(CoveragePerTestCache.class);
-      for (Entry<List<Integer>> entry : coveragePerTestCache.entries()) {
+      TestCaseCoverageCache testCaseCoverageCache = container.getComponentByType(TestCaseCoverageCache.class);
+      for (Entry<TestCaseCoverage> entry : testCaseCoverageCache.entries()) {
         String testFileKey = entry.key()[0].toString();
         if (!coveragePerTest.containsKey(testFileKey)) {
           coveragePerTest.put(testFileKey, new HashMap<String, Map<String, List<Integer>>>());
@@ -268,16 +269,16 @@ public class BatchMediumTester {
         if (!coveragePerTest.get(testFileKey).containsKey(testName)) {
           coveragePerTest.get(testFileKey).put(testName, new HashMap<String, List<Integer>>());
         }
-        coveragePerTest.get(testFileKey).get(testName).put(entry.key()[2].toString(), entry.value());
+        coveragePerTest.get(testFileKey).get(testName).put(entry.key()[2].toString(), entry.value().coveredLines());
       }
     }
 
     private void storeTestCases(ProjectScanContainer container) {
-      TestCaseCache testCaseCache = container.getComponentByType(TestCaseCache.class);
-      for (Entry<TestCase> entry : testCaseCache.entries()) {
+      TestCaseExecutionCache testCaseCache = container.getComponentByType(TestCaseExecutionCache.class);
+      for (Entry<TestCaseExecution> entry : testCaseCache.entries()) {
         String effectiveKey = entry.key()[0].toString();
         if (!testCasesPerFile.containsKey(effectiveKey)) {
-          testCasesPerFile.put(effectiveKey, new HashMap<String, TestCase>());
+          testCasesPerFile.put(effectiveKey, new HashMap<String, TestCaseExecution>());
         }
         testCasesPerFile.get(effectiveKey).put(entry.value().name(), entry.value());
       }
@@ -358,7 +359,7 @@ public class BatchMediumTester {
       return duplications.get(((DefaultInputFile) inputFile).key());
     }
 
-    public Collection<TestCase> testCasesFor(InputFile inputFile) {
+    public Collection<TestCaseExecution> testCasesFor(InputFile inputFile) {
       String key = ((DefaultInputFile) inputFile).key();
       if (testCasesPerFile.containsKey(key)) {
         return testCasesPerFile.get(key).values();
@@ -367,7 +368,7 @@ public class BatchMediumTester {
       }
     }
 
-    public TestCase testCase(InputFile inputFile, String testCaseName) {
+    public TestCaseExecution testCase(InputFile inputFile, String testCaseName) {
       return testCasesPerFile.get(((DefaultInputFile) inputFile).key()).get(testCaseName);
     }
 
index e482d8b86e88e843e81bd2b0a10b863d6ff00536..db57aa3d58989f8021bb17972be199b2bdecb812 100644 (file)
  */
 package org.sonar.batch.scan;
 
-import com.google.common.base.Preconditions;
 import org.sonar.api.batch.Sensor;
 import org.sonar.api.batch.SonarIndex;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.sensor.SensorContext;
@@ -33,8 +31,9 @@ import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.issue.Issue.Severity;
 import org.sonar.api.batch.sensor.measure.Measure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
 import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.config.Settings;
 import org.sonar.api.design.Dependency;
@@ -61,8 +60,6 @@ import org.sonar.batch.index.ComponentDataCache;
 import org.sonar.batch.scan2.BaseSensorContext;
 import org.sonar.core.component.ComponentKeys;
 
-import java.util.List;
-
 /**
  * Implements {@link SensorContext} but forward everything to {@link org.sonar.api.batch.SensorContext} for backward compatibility.
  * Will be dropped once old {@link Sensor} API is dropped.
@@ -178,8 +175,8 @@ public class SensorContextAdapter extends BaseSensorContext {
   }
 
   @Override
-  public void store(TestCase testCase) {
-    File testRes = getTestResource(((DefaultTestCase) testCase).testFile());
+  public void store(TestCaseExecution testCase) {
+    File testRes = getTestResource(((DefaultTestCaseExecution) testCase).testFile());
     MutableTestPlan testPlan = perspectives.as(MutableTestPlan.class, testRes);
     if (testPlan != null) {
       testPlan
@@ -193,16 +190,15 @@ public class SensorContextAdapter extends BaseSensorContext {
   }
 
   @Override
-  public void saveCoveragePerTest(TestCase testCase, InputFile coveredFile, List<Integer> coveredLines) {
-    Preconditions.checkArgument(coveredFile.type() == Type.MAIN, "Should be a main file: " + coveredFile);
-    File testRes = getTestResource(((DefaultTestCase) testCase).testFile());
-    File mainRes = getMainResource(coveredFile);
+  public void store(TestCaseCoverage testCaseCoverage) {
+    File testRes = getTestResource(testCaseCoverage.testFile());
+    File mainRes = getMainResource(testCaseCoverage.coveredFile());
     Testable testAbleFile = perspectives.as(MutableTestable.class, mainRes);
     if (testAbleFile != null) {
       MutableTestPlan testPlan = perspectives.as(MutableTestPlan.class, testRes);
       if (testPlan != null) {
-        for (MutableTestCase mutableTestCase : testPlan.testCasesByName(testCase.name())) {
-          mutableTestCase.setCoverageBlock(testAbleFile, coveredLines);
+        for (MutableTestCase mutableTestCase : testPlan.testCasesByName(testCaseCoverage.testName())) {
+          mutableTestCase.setCoverageBlock(testAbleFile, testCaseCoverage.coveredLines());
         }
       } else {
         throw new IllegalStateException("Unable to get MutableTestPlan perspective from " + testRes);
index bb81865c4b6404fdca8fb10efd6a0c8b9edfa343..c925f400100fb6ad8994631766edcedb77fa5fe9 100644 (file)
@@ -38,8 +38,10 @@ 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.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseCoverage;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
 import org.sonar.api.config.Settings;
 import org.sonar.batch.duplication.BlockCache;
 import org.sonar.batch.duplication.DefaultTokenBuilder;
@@ -153,8 +155,13 @@ public abstract class BaseSensorContext implements SensorContext, SensorStorage
   }
 
   @Override
-  public TestCase newTestCase() {
-    return new DefaultTestCase(this);
+  public TestCaseExecution newTestCaseExecution() {
+    return new DefaultTestCaseExecution(this);
+  }
+
+  @Override
+  public TestCaseCoverage newTestCaseCoverage() {
+    return new DefaultTestCaseCoverage(this);
   }
 
   @Override
index 90fc49ca395de73222fb2b6161f681ff96ce2e1d..58eaf6d5fa2f04642bca85c7f2e85848db26d4d4 100644 (file)
  */
 package org.sonar.batch.scan2;
 
-import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputFile.Type;
 import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.measure.Metric;
 import org.sonar.api.batch.rule.ActiveRules;
@@ -36,8 +34,9 @@ import org.sonar.api.batch.sensor.issue.Issue;
 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.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
 import org.sonar.api.config.Settings;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.rule.RuleKey;
@@ -48,8 +47,8 @@ import org.sonar.batch.duplication.DuplicationCache;
 import org.sonar.batch.index.ComponentDataCache;
 import org.sonar.batch.issue.IssueFilters;
 import org.sonar.batch.scan.SensorContextAdapter;
-import org.sonar.batch.test.CoveragePerTestCache;
-import org.sonar.batch.test.TestCaseCache;
+import org.sonar.batch.test.TestCaseCoverageCache;
+import org.sonar.batch.test.TestCaseExecutionCache;
 import org.sonar.core.component.ComponentKeys;
 
 import java.io.Serializable;
@@ -89,20 +88,20 @@ public class DefaultSensorContext extends BaseSensorContext {
   private final ProjectDefinition def;
   private final ActiveRules activeRules;
   private final IssueFilters issueFilters;
-  private final TestCaseCache testCaseCache;
-  private final CoveragePerTestCache coveragePerTestCache;
+  private final TestCaseExecutionCache testCaseExecutionCache;
+  private final TestCaseCoverageCache coveragePerTestCache;
   private final DependencyCache dependencyCache;
 
   public DefaultSensorContext(ProjectDefinition def, MeasureCache measureCache, IssueCache issueCache,
     Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache,
-    BlockCache blockCache, DuplicationCache duplicationCache, TestCaseCache testCaseCache, CoveragePerTestCache coveragePerTestCache, DependencyCache dependencyCache) {
+    BlockCache blockCache, DuplicationCache duplicationCache, TestCaseExecutionCache testCaseCache, TestCaseCoverageCache coveragePerTestCache, DependencyCache dependencyCache) {
     super(settings, fs, activeRules, componentDataCache, blockCache, duplicationCache);
     this.def = def;
     this.measureCache = measureCache;
     this.issueCache = issueCache;
     this.activeRules = activeRules;
     this.issueFilters = issueFilters;
-    this.testCaseCache = testCaseCache;
+    this.testCaseExecutionCache = testCaseCache;
     this.coveragePerTestCache = coveragePerTestCache;
     this.dependencyCache = dependencyCache;
   }
@@ -157,18 +156,19 @@ public class DefaultSensorContext extends BaseSensorContext {
   }
 
   @Override
-  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());
+  public void store(TestCaseExecution testCaseExecution) {
+    if (testCaseExecutionCache.contains(((DefaultTestCaseExecution) testCaseExecution).testFile(), testCaseExecution.name())) {
+      throw new IllegalArgumentException("There is already a test case with the same name: " + testCaseExecution.name());
     }
-    testCaseCache.put(((DefaultTestCase) testCase).testFile(), testCase);
+    testCaseExecutionCache.put(((DefaultTestCaseExecution) testCaseExecution).testFile(), testCaseExecution);
   }
 
   @Override
-  public void saveCoveragePerTest(TestCase testCase, InputFile coveredFile, List<Integer> coveredLines) {
-    Preconditions.checkNotNull(testCase);
-    Preconditions.checkArgument(coveredFile.type() == Type.MAIN, "Should be a main file: " + coveredFile);
-    coveragePerTestCache.put(testCase, coveredFile, coveredLines);
+  public void store(TestCaseCoverage testCaseCoverage) {
+    if (coveragePerTestCache.getCoverage(testCaseCoverage.testFile(), testCaseCoverage.testName(), testCaseCoverage.coveredFile()) != null) {
+      throw new IllegalArgumentException("Test coverage already registered for this combination of test file, test name and main file: " + testCaseCoverage);
+    }
+    coveragePerTestCache.put(testCaseCoverage);
   }
 
   @Override
index 431529be787ac0284514b9a4a143f3e115e4b619..0da1a2f8d0d1f38ba2f00491112b747786694aaa 100644 (file)
@@ -48,8 +48,8 @@ import org.sonar.batch.scan.ProjectSettings;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
 import org.sonar.batch.scan.maven.MavenPluginExecutor;
-import org.sonar.batch.test.CoveragePerTestCache;
-import org.sonar.batch.test.TestCaseCache;
+import org.sonar.batch.test.TestCaseCoverageCache;
+import org.sonar.batch.test.TestCaseExecutionCache;
 
 public class ProjectScanContainer extends ComponentContainer {
   public ProjectScanContainer(ComponentContainer taskContainer) {
@@ -118,8 +118,8 @@ public class ProjectScanContainer extends ComponentContainer {
       DuplicationCache.class,
 
       // Tests
-      TestCaseCache.class,
-      CoveragePerTestCache.class,
+      TestCaseExecutionCache.class,
+      TestCaseCoverageCache.class,
 
       // Dependencies
       DependencyCache.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/CoveragePerTestCache.java b/sonar-batch/src/main/java/org/sonar/batch/test/CoveragePerTestCache.java
deleted file mode 100644 (file)
index 211cfb4..0000000
+++ /dev/null
@@ -1,66 +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.batch.test;
-
-import com.google.common.base.Preconditions;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
-import org.sonar.batch.index.Cache;
-import org.sonar.batch.index.Cache.Entry;
-import org.sonar.batch.index.Caches;
-
-import javax.annotation.CheckForNull;
-
-import java.util.List;
-
-/**
- * Cache of coverage per test. This cache is shared amongst all project modules.
- */
-public class CoveragePerTestCache implements BatchComponent {
-
-  private final Cache<List<Integer>> cache;
-
-  public CoveragePerTestCache(Caches caches) {
-    cache = caches.createCache("coveragePerTest");
-  }
-
-  public Iterable<Entry<List<Integer>>> entries() {
-    return cache.entries();
-  }
-
-  @CheckForNull
-  public List<Integer> getCoveredLines(InputFile testFile, String testCaseName, InputFile mainFile) {
-    Preconditions.checkNotNull(testFile);
-    Preconditions.checkNotNull(testCaseName);
-    return cache.get(((DefaultInputFile) testFile).key(), testCaseName, ((DefaultInputFile) mainFile).key());
-  }
-
-  public CoveragePerTestCache put(TestCase testCase, InputFile mainFile, List<Integer> coveredLines) {
-    Preconditions.checkNotNull(testCase);
-    Preconditions.checkNotNull(mainFile);
-    Preconditions.checkNotNull(coveredLines);
-    cache.put(((DefaultInputFile) ((DefaultTestCase) testCase).testFile()).key(), testCase.name(), ((DefaultInputFile) mainFile).key(), coveredLines);
-    return this;
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseCoverageValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseCoverageValueCoder.java
new file mode 100644 (file)
index 0000000..69b7845
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.batch.test;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseCoverage;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class DefaultTestCaseCoverageValueCoder implements ValueCoder {
+
+  private InputPathCache inputPathCache;
+
+  public DefaultTestCaseCoverageValueCoder(InputPathCache inputPathCache) {
+    this.inputPathCache = inputPathCache;
+  }
+
+  @Override
+  public void put(Value value, Object object, CoderContext context) {
+    DefaultTestCaseCoverage t = (DefaultTestCaseCoverage) object;
+    value.putUTF(((DefaultInputFile) t.testFile()).moduleKey());
+    value.putUTF(((DefaultInputFile) t.testFile()).relativePath());
+    value.putUTF(t.testName());
+    value.putUTF(((DefaultInputFile) t.coveredFile()).moduleKey());
+    value.putUTF(((DefaultInputFile) t.coveredFile()).relativePath());
+    value.put(t.coveredLines().size());
+    for (Integer line : t.coveredLines()) {
+      value.put(line.intValue());
+    }
+  }
+
+  @Override
+  public Object get(Value value, Class clazz, CoderContext context) {
+    String testModuleKey = value.getString();
+    String testRelativePath = value.getString();
+    InputFile testFile = inputPathCache.getFile(testModuleKey, testRelativePath);
+    if (testFile == null) {
+      throw new IllegalStateException("Unable to load InputFile " + testModuleKey + ":" + testRelativePath);
+    }
+    String name = value.getString();
+    String mainModuleKey = value.getString();
+    String mainRelativePath = value.getString();
+    InputFile mainFile = inputPathCache.getFile(mainModuleKey, mainRelativePath);
+    if (mainFile == null) {
+      throw new IllegalStateException("Unable to load InputFile " + mainModuleKey + ":" + mainRelativePath);
+    }
+    int size = value.getInt();
+    List<Integer> lines = new ArrayList<Integer>(size);
+    for (int i = 0; i < size; i++) {
+      lines.add(value.getInt());
+    }
+    return new DefaultTestCaseCoverage()
+      .testFile(testFile)
+      .testName(name)
+      .cover(mainFile)
+      .onLines(lines);
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseExecutionValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseExecutionValueCoder.java
new file mode 100644 (file)
index 0000000..9b0bcf7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.batch.test;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+
+import javax.annotation.Nullable;
+
+class DefaultTestCaseExecutionValueCoder implements ValueCoder {
+
+  private InputPathCache inputPathCache;
+
+  public DefaultTestCaseExecutionValueCoder(InputPathCache inputPathCache) {
+    this.inputPathCache = inputPathCache;
+  }
+
+  @Override
+  public void put(Value value, Object object, CoderContext context) {
+    DefaultTestCaseExecution t = (DefaultTestCaseExecution) object;
+    value.putUTF(((DefaultInputFile) t.testFile()).moduleKey());
+    value.putUTF(((DefaultInputFile) t.testFile()).relativePath());
+    value.putUTF(t.name());
+    putUTFOrNull(value, t.message());
+    putUTFOrNull(value, t.stackTrace());
+    Long durationInMs = t.durationInMs();
+    value.put(durationInMs != null ? durationInMs.longValue() : -1);
+    value.put(t.type().ordinal());
+    value.put(t.status().ordinal());
+  }
+
+  private void putUTFOrNull(Value value, @Nullable String utfOrNull) {
+    if (utfOrNull != null) {
+      value.putUTF(utfOrNull);
+    } else {
+      value.putNull();
+    }
+  }
+
+  @Override
+  public Object get(Value value, Class clazz, CoderContext context) {
+    String moduleKey = value.getString();
+    String relativePath = value.getString();
+    InputFile testFile = inputPathCache.getFile(moduleKey, relativePath);
+    if (testFile == null) {
+      throw new IllegalStateException("Unable to load InputFile " + moduleKey + ":" + relativePath);
+    }
+    String name = value.getString();
+    String message = value.getString();
+    String stack = value.getString();
+    long duration = value.getLong();
+    TestCaseExecution.Type type = TestCaseExecution.Type.values()[value.getInt()];
+    TestCaseExecution.Status status = TestCaseExecution.Status.values()[value.getInt()];
+    return new DefaultTestCaseExecution()
+      .inTestFile(testFile)
+      .ofType(type)
+      .name(name)
+      .durationInMs(duration != -1 ? duration : null)
+      .status(status)
+      .message(message)
+      .stackTrace(stack);
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/test/DefaultTestCaseValueCoder.java
deleted file mode 100644 (file)
index 402cc62..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.batch.test;
-
-import com.persistit.Value;
-import com.persistit.encoding.CoderContext;
-import com.persistit.encoding.ValueCoder;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
-import org.sonar.batch.scan.filesystem.InputPathCache;
-
-import javax.annotation.Nullable;
-
-class DefaultTestCaseValueCoder implements ValueCoder {
-
-  private InputPathCache inputPathCache;
-
-  public DefaultTestCaseValueCoder(InputPathCache inputPathCache) {
-    this.inputPathCache = inputPathCache;
-  }
-
-  @Override
-  public void put(Value value, Object object, CoderContext context) {
-    DefaultTestCase t = (DefaultTestCase) object;
-    value.putUTF(((DefaultInputFile) t.testFile()).moduleKey());
-    value.putUTF(((DefaultInputFile) t.testFile()).relativePath());
-    value.putUTF(t.name());
-    putUTFOrNull(value, t.message());
-    putUTFOrNull(value, t.stackTrace());
-    Long durationInMs = t.durationInMs();
-    value.put(durationInMs != null ? durationInMs.longValue() : -1);
-    value.put(t.type().ordinal());
-    value.put(t.status().ordinal());
-  }
-
-  private void putUTFOrNull(Value value, @Nullable String utfOrNull) {
-    if (utfOrNull != null) {
-      value.putUTF(utfOrNull);
-    } else {
-      value.putNull();
-    }
-  }
-
-  @Override
-  public Object get(Value value, Class clazz, CoderContext context) {
-    String moduleKey = value.getString();
-    String relativePath = value.getString();
-    InputFile testFile = inputPathCache.getFile(moduleKey, relativePath);
-    if (testFile == null) {
-      throw new IllegalStateException("Unable to load InputFile " + moduleKey + ":" + relativePath);
-    }
-    String name = value.getString();
-    String message = value.getString();
-    String stack = value.getString();
-    long duration = value.getLong();
-    TestCase.Type type = TestCase.Type.values()[value.getInt()];
-    TestCase.Status status = TestCase.Status.values()[value.getInt()];
-    return new DefaultTestCase()
-      .inTestFile(testFile)
-      .ofType(type)
-      .name(name)
-      .durationInMs(duration != -1 ? duration : null)
-      .status(status)
-      .message(message)
-      .stackTrace(stack);
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/TestCaseCache.java b/sonar-batch/src/main/java/org/sonar/batch/test/TestCaseCache.java
deleted file mode 100644 (file)
index e2de947..0000000
+++ /dev/null
@@ -1,69 +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.batch.test;
-
-import com.google.common.base.Preconditions;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.test.TestCase;
-import org.sonar.api.batch.sensor.test.internal.DefaultTestCase;
-import org.sonar.batch.index.Cache;
-import org.sonar.batch.index.Cache.Entry;
-import org.sonar.batch.index.Caches;
-import org.sonar.batch.scan.filesystem.InputPathCache;
-
-import javax.annotation.CheckForNull;
-
-/**
- * Cache of all TestCases. This cache is shared amongst all project modules.
- */
-public class TestCaseCache implements BatchComponent {
-
-  private final Cache<TestCase> cache;
-
-  public TestCaseCache(Caches caches, InputPathCache inputPathCache) {
-    caches.registerValueCoder(DefaultTestCase.class, new DefaultTestCaseValueCoder(inputPathCache));
-    cache = caches.createCache("testCases");
-  }
-
-  public Iterable<Entry<TestCase>> entries() {
-    return cache.entries();
-  }
-
-  @CheckForNull
-  public TestCase get(InputFile testFile, String testCaseName) {
-    Preconditions.checkNotNull(testFile);
-    Preconditions.checkNotNull(testCaseName);
-    return cache.get(((DefaultInputFile) testFile).key(), testCaseName);
-  }
-
-  public TestCaseCache put(InputFile testFile, TestCase testCase) {
-    Preconditions.checkNotNull(testFile);
-    Preconditions.checkNotNull(testCase);
-    cache.put(((DefaultInputFile) testFile).key(), testCase.name(), testCase);
-    return this;
-  }
-
-  public boolean contains(InputFile testFile, String name) {
-    return cache.containsKey(((DefaultInputFile) testFile).key(), name);
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/TestCaseCoverageCache.java b/sonar-batch/src/main/java/org/sonar/batch/test/TestCaseCoverageCache.java
new file mode 100644 (file)
index 0000000..7e52932
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.batch.test;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseCoverage;
+import org.sonar.batch.index.Cache;
+import org.sonar.batch.index.Cache.Entry;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * Cache of coverage per test. This cache is shared amongst all project modules.
+ */
+public class TestCaseCoverageCache implements BatchComponent {
+
+  private final Cache<TestCaseCoverage> cache;
+
+  public TestCaseCoverageCache(Caches caches, InputPathCache inputPathCache) {
+    caches.registerValueCoder(DefaultTestCaseCoverage.class, new DefaultTestCaseCoverageValueCoder(inputPathCache));
+    cache = caches.createCache("testCaseCoverage");
+  }
+
+  public Iterable<Entry<TestCaseCoverage>> entries() {
+    return cache.entries();
+  }
+
+  @CheckForNull
+  public TestCaseCoverage getCoverage(InputFile testFile, String testCaseName, InputFile mainFile) {
+    Preconditions.checkNotNull(testFile);
+    Preconditions.checkNotNull(testCaseName);
+    Preconditions.checkNotNull(mainFile);
+    return cache.get(((DefaultInputFile) testFile).key(), testCaseName, ((DefaultInputFile) mainFile).key());
+  }
+
+  public TestCaseCoverageCache put(TestCaseCoverage coverage) {
+    Preconditions.checkNotNull(coverage);
+    cache.put(((DefaultInputFile) coverage.testFile()).key(), coverage.testName(), ((DefaultInputFile) coverage.coveredFile()).key(), coverage);
+    return this;
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/test/TestCaseExecutionCache.java b/sonar-batch/src/main/java/org/sonar/batch/test/TestCaseExecutionCache.java
new file mode 100644 (file)
index 0000000..02dab1c
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.batch.test;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
+import org.sonar.batch.index.Cache;
+import org.sonar.batch.index.Cache.Entry;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * Cache of all TestCases. This cache is shared amongst all project modules.
+ */
+public class TestCaseExecutionCache implements BatchComponent {
+
+  private final Cache<TestCaseExecution> cache;
+
+  public TestCaseExecutionCache(Caches caches, InputPathCache inputPathCache) {
+    caches.registerValueCoder(DefaultTestCaseExecution.class, new DefaultTestCaseExecutionValueCoder(inputPathCache));
+    cache = caches.createCache("testCaseExecutions");
+  }
+
+  public Iterable<Entry<TestCaseExecution>> entries() {
+    return cache.entries();
+  }
+
+  @CheckForNull
+  public TestCaseExecution get(InputFile testFile, String testCaseName) {
+    Preconditions.checkNotNull(testFile);
+    Preconditions.checkNotNull(testCaseName);
+    return cache.get(((DefaultInputFile) testFile).key(), testCaseName);
+  }
+
+  public TestCaseExecutionCache put(InputFile testFile, TestCaseExecution testCase) {
+    Preconditions.checkNotNull(testFile);
+    Preconditions.checkNotNull(testCase);
+    cache.put(((DefaultInputFile) testFile).key(), testCase.name(), testCase);
+    return this;
+  }
+
+  public boolean contains(InputFile testFile, String name) {
+    return cache.containsKey(((DefaultInputFile) testFile).key(), name);
+  }
+
+}
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore
new file mode 100644 (file)
index 0000000..ecbefd4
--- /dev/null
@@ -0,0 +1 @@
+.sonar
index 831ea878114bbf70f34ba20a8ffd4341702f88cb..865fff64c9e401ac1e1b82ffe4f9b9095336f117 100644 (file)
@@ -30,7 +30,8 @@ import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
 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.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
 import org.sonar.api.config.Settings;
 
 import java.io.Serializable;
@@ -112,21 +113,18 @@ public interface SensorContext {
   // ------------ TESTS ------------
 
   /**
-   * Create a new test case.
-   * Don't forget to call {@link TestCase#save()} once all parameters are provided.
+   * Create a new test case execution report.
+   * Don't forget to call {@link TestCaseExecution#save()} once all parameters are provided.
    * @since 5.0
    */
-  TestCase newTestCase();
+  TestCaseExecution newTestCaseExecution();
 
   /**
-   * Register coverage of a given test case on another main file. TestCase should have been registered using {@link #testPlanBuilder(InputFile)}
-   * @param testFile test file containing the test case
-   * @param testCaseName name of the test case
-   * @param coveredFile main file that is covered
-   * @param coveredLines list of covered lines
+   * Create a new test case coverage report.
+   * Don't forget to call {@link TestCaseCoverage#save()} once all parameters are provided.
    * @since 5.0
    */
-  void saveCoveragePerTest(TestCase testCase, InputFile coveredFile, List<Integer> coveredLines);
+  TestCaseCoverage newTestCaseCoverage();
 
   // ------------ DEPENDENCIES ------------
 
index 7faec27f4c674567a29c4fdd576c46077b881aa6..3eb8612d94b251717a58844828b0e58b35142ed4 100644 (file)
@@ -22,7 +22,8 @@ package org.sonar.api.batch.sensor;
 import org.sonar.api.batch.sensor.dependency.Dependency;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
-import org.sonar.api.batch.sensor.test.TestCase;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
 
 /**
  * Interface for storing data computed by sensors.
@@ -34,8 +35,10 @@ public interface SensorStorage {
 
   void store(Issue issue);
 
-  void store(TestCase testCase);
+  void store(TestCaseExecution testCaseExecution);
 
   void store(Dependency dependency);
 
+  void store(TestCaseCoverage testCaseCoverage);
+
 }
index 34a91f8075624386997afea5779bb047dfa6e510..9915ab48e92532c3c9da7e30099e812952e13e3b 100644 (file)
@@ -22,28 +22,26 @@ package org.sonar.api.batch.sensor.dependency.internal;
 import com.google.common.base.Preconditions;
 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.dependency.Dependency;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
 
 import javax.annotation.Nullable;
 
-public class DefaultDependency implements Dependency {
+public class DefaultDependency extends DefaultStorable implements Dependency {
 
-  private final SensorStorage storage;
   private InputFile from;
   private InputFile to;
   private int weight = 1;
   private boolean saved = false;
 
   public DefaultDependency() {
-    this.storage = null;
+    super(null);
   }
 
   public DefaultDependency(@Nullable SensorStorage storage) {
-    this.storage = storage;
+    super(storage);
   }
 
   @Override
@@ -68,14 +66,11 @@ public class DefaultDependency implements Dependency {
   }
 
   @Override
-  public void save() {
-    Preconditions.checkNotNull(this.storage, "No persister on this object");
-    Preconditions.checkState(!saved, "This dependency was already saved");
+  public void doSave() {
     Preconditions.checkState(!this.from.equals(this.to), "From and To can't be the same inputFile");
     Preconditions.checkNotNull(this.from, "From inputFile can't be null");
     Preconditions.checkNotNull(this.to, "To inputFile can't be null");
     storage.store((Dependency) this);
-    this.saved = true;
   }
 
   @Override
@@ -123,9 +118,4 @@ public class DefaultDependency implements Dependency {
       toHashCode();
   }
 
-  @Override
-  public String toString() {
-    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
-  }
-
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultStorable.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultStorable.java
new file mode 100644 (file)
index 0000000..9b1c9fa
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.internal;
+
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.batch.sensor.SensorStorage;
+
+import javax.annotation.Nullable;
+
+public abstract class DefaultStorable {
+
+  protected transient final SensorStorage storage;
+  private transient boolean saved = false;
+
+  public DefaultStorable() {
+    this.storage = null;
+  }
+
+  public DefaultStorable(@Nullable SensorStorage storage) {
+    this.storage = storage;
+  }
+
+  public final void save() {
+    Preconditions.checkNotNull(this.storage, "No persister on this object");
+    Preconditions.checkState(!saved, "This measure was already saved");
+    doSave();
+    this.saved = true;
+  }
+
+  protected abstract void doSave();
+
+  @Override
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+  }
+
+}
index 42637fdd4cbc86de550c97eb7ad2dedc576abb76..ab7912cc8a839ccb9c8d693dd4626a62aac48f46 100644 (file)
@@ -21,12 +21,11 @@ package org.sonar.api.batch.sensor.issue.internal;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
 import org.sonar.api.batch.fs.InputDir;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputPath;
 import org.sonar.api.batch.sensor.SensorStorage;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.rule.RuleKey;
 
@@ -35,7 +34,7 @@ import javax.annotation.Nullable;
 
 import java.util.UUID;
 
-public class DefaultIssue implements Issue {
+public class DefaultIssue extends DefaultStorable implements Issue {
 
   private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null";
   private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null";
@@ -49,16 +48,15 @@ public class DefaultIssue implements Issue {
   private Integer line;
   private Double effortToFix;
   private Severity overridenSeverity;
-  private final SensorStorage storage;
 
   public DefaultIssue() {
+    super(null);
     this.key = UUID.randomUUID().toString();
-    this.storage = null;
   }
 
   public DefaultIssue(SensorStorage storage) {
+    super(storage);
     this.key = UUID.randomUUID().toString();
-    this.storage = storage;
   }
 
   @Override
@@ -154,11 +152,9 @@ public class DefaultIssue implements Issue {
   }
 
   @Override
-  public void save() {
-    Preconditions.checkNotNull(this.storage, "No persister on this object");
+  public void doSave() {
     Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue");
     Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key");
-
     storage.store(this);
   }
 
@@ -187,8 +183,4 @@ public class DefaultIssue implements Issue {
     return key.hashCode();
   }
 
-  @Override
-  public String toString() {
-    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
-  }
 }
index 29b5c92bc2c1e8817d0b0c8e448f98a0965ccfa3..167f9e6af98385e859984b06d27617a8fdd44cba 100644 (file)
@@ -22,11 +22,10 @@ package org.sonar.api.batch.sensor.measure.internal;
 import com.google.common.base.Preconditions;
 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.measure.Metric;
 import org.sonar.api.batch.sensor.SensorStorage;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
 import org.sonar.api.batch.sensor.measure.Measure;
 
 import javax.annotation.CheckForNull;
@@ -34,22 +33,20 @@ import javax.annotation.Nullable;
 
 import java.io.Serializable;
 
-public class DefaultMeasure<G extends Serializable> implements Measure<G> {
+public class DefaultMeasure<G extends Serializable> extends DefaultStorable implements Measure<G> {
 
-  private final SensorStorage storage;
   private boolean onProject = false;
   private InputFile file;
   private Metric<G> metric;
   private G value;
-  private boolean saved = false;
   private boolean fromCore = false;
 
   public DefaultMeasure() {
-    this.storage = null;
+    super();
   }
 
   public DefaultMeasure(@Nullable SensorStorage storage) {
-    this.storage = storage;
+    super(storage);
   }
 
   @Override
@@ -101,14 +98,11 @@ public class DefaultMeasure<G extends Serializable> implements Measure<G> {
   }
 
   @Override
-  public void save() {
-    Preconditions.checkNotNull(this.storage, "No persister on this object");
-    Preconditions.checkState(!saved, "This measure was already saved");
+  public void doSave() {
     Preconditions.checkNotNull(this.value, "Measure value can't be null");
     Preconditions.checkNotNull(this.metric, "Measure metric can't be null");
     Preconditions.checkState(this.metric.valueType().equals(this.value.getClass()), "Measure value should be of type " + this.metric.valueType());
     storage.store((Measure<Serializable>) this);
-    this.saved = true;
   }
 
   @Override
@@ -157,9 +151,4 @@ public class DefaultMeasure<G extends Serializable> implements Measure<G> {
       toHashCode();
   }
 
-  @Override
-  public String toString() {
-    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
-  }
-
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCase.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCase.java
deleted file mode 100644 (file)
index 37b1047..0000000
+++ /dev/null
@@ -1,125 +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.fs.InputFile;
-
-import javax.annotation.Nullable;
-
-/**
- * Represents a single test in a test file.
- * @since 5.0
- */
-public interface TestCase {
-
-  /**
-   * Test execution status.
-   */
-  enum Status {
-    OK, FAILURE, ERROR, SKIPPED;
-
-    public static Status of(@Nullable String s) {
-      return s == null ? null : valueOf(s.toUpperCase());
-    }
-  }
-
-  /**
-   * 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();
-
-  /**
-   * 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.
-   */
-  Status status();
-
-  /**
-   * Status of execution of the test.
-   */
-  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/TestCaseCoverage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseCoverage.java
new file mode 100644 (file)
index 0000000..4d62042
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.fs.InputFile;
+
+import java.util.List;
+
+/**
+ * Represents line coverage produced by a single test in a test file on a single main file.
+ * @since 5.0
+ */
+public interface TestCaseCoverage {
+
+  /**
+   * InputFile where this test is located.
+   */
+  InputFile testFile();
+
+  /**
+   * Set file where this test is located. Mandatory.
+   */
+  TestCaseCoverage testFile(InputFile testFile);
+
+  /**
+   * Name of this test case.
+   */
+  String testName();
+
+  /**
+   * Set name of this test. Name is mandatory.
+   */
+  TestCaseCoverage testName(String name);
+
+  /**
+   * InputFile covered by this test.
+   */
+  InputFile coveredFile();
+
+  /**
+   * Set file covered by this test. Mandatory.
+   */
+  TestCaseCoverage cover(InputFile mainFile);
+
+  /**
+   * List of line numbers (1-based) covered by this test.
+   */
+  List<Integer> coveredLines();
+
+  /**
+   * Set list of line numbers (1-based) covered by this test. Mandatory.
+   */
+  TestCaseCoverage onLines(List<Integer> lines);
+
+  /**
+   * Call this method only once when your are done with defining the test case coverage.
+   */
+  void save();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseExecution.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/TestCaseExecution.java
new file mode 100644 (file)
index 0000000..9a9bf4b
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.fs.InputFile;
+
+import javax.annotation.Nullable;
+
+/**
+ * Represents result of execution of a single test in a test file.
+ * @since 5.0
+ */
+public interface TestCaseExecution {
+
+  /**
+   * Test execution status.
+   */
+  enum Status {
+    OK, FAILURE, ERROR, SKIPPED;
+
+    public static Status of(@Nullable String s) {
+      return s == null ? null : valueOf(s.toUpperCase());
+    }
+  }
+
+  /**
+   * Test type.
+   */
+  enum Type {
+    UNIT, INTEGRATION;
+  }
+
+  /**
+   * InputFile where this test is located.
+   */
+  InputFile testFile();
+
+  /**
+   * Set file where this test is located. Mandatory.
+   */
+  TestCaseExecution inTestFile(InputFile testFile);
+
+  /**
+   * Duration in milliseconds
+   */
+  Long durationInMs();
+
+  /**
+   * Duration in milliseconds
+   */
+  TestCaseExecution durationInMs(long duration);
+
+  /**
+   * Name of this test case.
+   */
+  String name();
+
+  /**
+   * Set name of this test. Name is mandatory.
+   */
+  TestCaseExecution name(String name);
+
+  /**
+   * Status of execution of the test.
+   */
+  Status status();
+
+  /**
+   * Status of execution of the test.
+   */
+  TestCaseExecution 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}).
+   */
+  TestCaseExecution message(String message);
+
+  /**
+   * Type of test.
+   */
+  Type type();
+
+  /**
+   * Type of test.
+   */
+  TestCaseExecution ofType(Type type);
+
+  /**
+   * Stacktrace (usually in case of {@link Status#ERROR}).
+   */
+  String stackTrace();
+
+  /**
+   * Set stacktrace (usually in case of {@link Status#ERROR}).
+   */
+  TestCaseExecution stackTrace(String stackTrace);
+
+  /**
+   * Call this method only once when your are done with defining the test case execution.
+   */
+  void save();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCase.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCase.java
deleted file mode 100644 (file)
index 100b054..0000000
+++ /dev/null
@@ -1,198 +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.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;
-import javax.annotation.Nullable;
-
-public class DefaultTestCase implements TestCase {
-
-  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() {
-    this.storage = null;
-  }
-
-  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;
-  }
-
-  @Override
-  public InputFile testFile() {
-    return testFile;
-  }
-
-  @CheckForNull
-  @Override
-  public Long durationInMs() {
-    return duration;
-  }
-
-  @Override
-  public Type type() {
-    return type;
-  }
-
-  @Override
-  public Status status() {
-    return status;
-  }
-
-  @Override
-  public String name() {
-    return name;
-  }
-
-  @CheckForNull
-  @Override
-  public String message() {
-    return message;
-  }
-
-  @CheckForNull
-  @Override
-  public String stackTrace() {
-    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) {
-    if (obj == null) {
-      return false;
-    }
-    if (obj == this) {
-      return true;
-    }
-    if (obj.getClass() != getClass()) {
-      return false;
-    }
-    DefaultTestCase rhs = (DefaultTestCase) obj;
-    return new EqualsBuilder()
-      .append(testFile, rhs.testFile)
-      .append(name, rhs.name)
-      .append(duration, rhs.duration)
-      .append(status, rhs.status)
-      .append(message, rhs.message)
-      .append(type, rhs.type)
-      .append(stackTrace, rhs.stackTrace)
-      .isEquals();
-  }
-
-  @Override
-  public int hashCode() {
-    return new HashCodeBuilder(13, 43)
-      .append(testFile)
-      .append(name)
-      .append(duration)
-      .append(status)
-      .append(message)
-      .append(type)
-      .append(stackTrace)
-      .toHashCode();
-  }
-
-  @Override
-  public String toString() {
-    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
-      .append("file", testFile)
-      .append("name", name)
-      .append("duration", duration)
-      .append("status", status)
-      .append("message", message)
-      .append("type", type)
-      .append("stackTrace", stackTrace)
-      .toString();
-  }
-
-}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseCoverage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseCoverage.java
new file mode 100644 (file)
index 0000000..f2f8834
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.SensorStorage;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
+import org.sonar.api.batch.sensor.test.TestCaseCoverage;
+
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultTestCaseCoverage extends DefaultStorable implements TestCaseCoverage {
+
+  private InputFile testFile;
+  private InputFile mainFile;
+  private String name;
+  private List<Integer> lines;
+
+  public DefaultTestCaseCoverage() {
+    super(null);
+  }
+
+  public DefaultTestCaseCoverage(SensorStorage storage) {
+    super(storage);
+  }
+
+  @Override
+  public InputFile testFile() {
+    return testFile;
+  }
+
+  @Override
+  public DefaultTestCaseCoverage testFile(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 InputFile coveredFile() {
+    return mainFile;
+  }
+
+  @Override
+  public TestCaseCoverage cover(InputFile mainFile) {
+    Preconditions.checkNotNull(mainFile, "InputFile cannot be null");
+    Preconditions.checkArgument(mainFile.type() == InputFile.Type.MAIN, "Should be a main file: " + mainFile);
+    this.mainFile = mainFile;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCaseCoverage testName(String name) {
+    Preconditions.checkArgument(StringUtils.isNotBlank(name), "Test name is mandatory and should not be blank");
+    this.name = name;
+    return this;
+  }
+
+  @Override
+  public String testName() {
+    return name;
+  }
+
+  @Override
+  public List<Integer> coveredLines() {
+    return Collections.unmodifiableList(lines);
+  }
+
+  @Override
+  public TestCaseCoverage onLines(List<Integer> lines) {
+    Preconditions.checkNotNull(lines, "Lines list cannot be null");
+    Preconditions.checkArgument(lines.size() > 0, "No need to register test coverage if no line is covered");
+    this.lines = lines;
+    return this;
+  }
+
+  @Override
+  public void doSave() {
+    Preconditions.checkNotNull(testFile, "TestFile is mandatory");
+    Preconditions.checkNotNull(mainFile, "MainFile is mandatory");
+    Preconditions.checkNotNull(name, "Test name is mandatory");
+    Preconditions.checkNotNull(lines, "Lines are mandatory");
+    storage.store(this);
+  }
+
+  // Just for unit tests
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (obj == this) {
+      return true;
+    }
+    if (obj.getClass() != getClass()) {
+      return false;
+    }
+    DefaultTestCaseCoverage rhs = (DefaultTestCaseCoverage) obj;
+    return new EqualsBuilder()
+      .append(testFile, rhs.testFile)
+      .append(name, rhs.name)
+      .append(mainFile, rhs.mainFile)
+      .append(lines.toArray(), rhs.lines.toArray())
+      .isEquals();
+  }
+
+  @Override
+  public int hashCode() {
+    return new HashCodeBuilder(13, 43)
+      .append(testFile)
+      .append(name)
+      .append(mainFile)
+      .toHashCode();
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseExecution.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseExecution.java
new file mode 100644 (file)
index 0000000..550fd83
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.SensorStorage;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
+import org.sonar.api.batch.sensor.test.TestCaseExecution;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class DefaultTestCaseExecution extends DefaultStorable implements TestCaseExecution {
+
+  private InputFile testFile;
+  private String name;
+  private Long duration;
+  private TestCaseExecution.Status status = Status.OK;
+  private String message;
+  private TestCaseExecution.Type type = Type.UNIT;
+  private String stackTrace;
+
+  public DefaultTestCaseExecution() {
+    super(null);
+  }
+
+  public DefaultTestCaseExecution(SensorStorage storage) {
+    super(storage);
+  }
+
+  @Override
+  public DefaultTestCaseExecution 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 DefaultTestCaseExecution name(String name) {
+    Preconditions.checkArgument(StringUtils.isNotBlank(name), "Test name is mandatory and should not be blank");
+    this.name = name;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCaseExecution durationInMs(long duration) {
+    Preconditions.checkArgument(duration >= 0, "Test duration must be positive (got: " + duration + ")");
+    this.duration = duration;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCaseExecution status(TestCaseExecution.Status status) {
+    Preconditions.checkNotNull(status);
+    this.status = status;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCaseExecution message(@Nullable String message) {
+    this.message = message;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCaseExecution ofType(TestCaseExecution.Type type) {
+    Preconditions.checkNotNull(type);
+    this.type = type;
+    return this;
+  }
+
+  @Override
+  public DefaultTestCaseExecution stackTrace(@Nullable String stackTrace) {
+    this.stackTrace = stackTrace;
+    return this;
+  }
+
+  @Override
+  public InputFile testFile() {
+    return testFile;
+  }
+
+  @CheckForNull
+  @Override
+  public Long durationInMs() {
+    return duration;
+  }
+
+  @Override
+  public Type type() {
+    return type;
+  }
+
+  @Override
+  public Status status() {
+    return status;
+  }
+
+  @Override
+  public String name() {
+    return name;
+  }
+
+  @CheckForNull
+  @Override
+  public String message() {
+    return message;
+  }
+
+  @CheckForNull
+  @Override
+  public String stackTrace() {
+    return stackTrace;
+  }
+
+  @Override
+  public void doSave() {
+    Preconditions.checkNotNull(testFile, "TestFile is mandatory");
+    Preconditions.checkNotNull(name, "Test name is mandatory");
+    storage.store(this);
+  }
+
+  // Just for unit tests
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (obj == this) {
+      return true;
+    }
+    if (obj.getClass() != getClass()) {
+      return false;
+    }
+    DefaultTestCaseExecution rhs = (DefaultTestCaseExecution) obj;
+    return new EqualsBuilder()
+      .append(testFile, rhs.testFile)
+      .append(name, rhs.name)
+      .append(duration, rhs.duration)
+      .append(status, rhs.status)
+      .append(message, rhs.message)
+      .append(type, rhs.type)
+      .append(stackTrace, rhs.stackTrace)
+      .isEquals();
+  }
+
+  @Override
+  public int hashCode() {
+    return new HashCodeBuilder(13, 43)
+      .append(testFile)
+      .append(name)
+      .append(duration)
+      .append(status)
+      .append(message)
+      .append(type)
+      .append(stackTrace)
+      .toHashCode();
+  }
+}
index 70dccba397670fe92e208c4730cbab0079f0035e..15dd571699e7b49383ad5475d1fa8fcc50748843 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.api.test;
 import javax.annotation.Nullable;
 
 /**
- * @deprecated since 5.0 see {@link org.sonar.api.batch.sensor.test.TestCase}
+ * @deprecated since 5.0 see {@link org.sonar.api.batch.sensor.test.TestCaseExecution}
  */
 @Deprecated
 public interface TestCase {
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseExecutionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseExecutionTest.java
new file mode 100644 (file)
index 0000000..01abb79
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.TestCaseExecution.Status;
+import org.sonar.api.batch.sensor.test.TestCaseExecution.Type;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DefaultTestCaseExecutionTest {
+
+  @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 {
+    DefaultTestCaseExecution testCase = new DefaultTestCaseExecution(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 testCreationWithDefaultValues() throws Exception {
+    DefaultTestCaseExecution testCase = new DefaultTestCaseExecution(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 {
+    DefaultTestCaseExecution builder = new DefaultTestCaseExecution(null)
+      .inTestFile(parent)
+      .name("myTest");
+
+    thrown.expect(IllegalArgumentException.class);
+
+    builder.durationInMs(-3);
+  }
+
+  @Test
+  public void testEqualsHashCodeToString() {
+    DefaultTestCaseExecution testCase1 = new DefaultTestCaseExecution(null)
+      .inTestFile(parent)
+      .name("myTest")
+      .durationInMs(1)
+      .message("message")
+      .stackTrace("stack")
+      .status(Status.ERROR)
+      .ofType(Type.UNIT);
+    DefaultTestCaseExecution testCase1a = new DefaultTestCaseExecution(null)
+      .inTestFile(parent)
+      .name("myTest")
+      .durationInMs(1)
+      .message("message")
+      .stackTrace("stack")
+      .status(Status.ERROR)
+      .ofType(Type.UNIT);
+    DefaultTestCaseExecution testCase2 = new DefaultTestCaseExecution(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);
+    assertThat(testCase1).isNotEqualTo(testCase2);
+    assertThat(testCase1).isNotEqualTo(null);
+    assertThat(testCase1).isNotEqualTo("foo");
+
+    assertThat(testCase1.toString()).isEqualTo(
+      "DefaultTestCaseExecution[testFile=[moduleKey=foo, relative=src/Foo.php, abs=null],name=myTest,duration=1,status=ERROR,message=message,type=UNIT,stackTrace=stack]");
+    assertThat(testCase1.hashCode()).isEqualTo(testCase1a.hashCode());
+  }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/test/internal/DefaultTestCaseTest.java
deleted file mode 100644 (file)
index a987eb1..0000000
+++ /dev/null
@@ -1,123 +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 DefaultTestCaseTest {
-
-  @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 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(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);
-    assertThat(testCase1).isNotEqualTo(testCase2);
-    assertThat(testCase1).isNotEqualTo(null);
-    assertThat(testCase1).isNotEqualTo("foo");
-
-    assertThat(testCase1.toString()).isEqualTo(
-      "DefaultTestCase[file=[moduleKey=foo, relative=src/Foo.php, abs=null],name=myTest,duration=1,status=ERROR,message=message,type=UNIT,stackTrace=stack]");
-    assertThat(testCase1.hashCode()).isEqualTo(testCase1a.hashCode());
-  }
-
-}