aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java5
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SignificantCodeSensor.java94
-rw-r--r--plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java2
-rw-r--r--plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SignificantCodeSensorTest.java91
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesHashRepository.java2
-rw-r--r--tests/projects/significantCode/sample-xoo/file.xoo11
-rw-r--r--tests/projects/significantCode/sample-xoo/file_additional_line.xoo12
-rw-r--r--tests/projects/significantCode/sample-xoo/file_changed.xoo11
-rw-r--r--tests/src/test/java/org/sonarqube/tests/source/SignificantCodeTest.java223
9 files changed, 448 insertions, 3 deletions
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
index 9cb185a943e..3b626b8ed9f 100644
--- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
@@ -34,6 +34,7 @@ import org.sonar.xoo.global.GlobalSensor;
import org.sonar.xoo.lang.CpdTokenizerSensor;
import org.sonar.xoo.lang.LineMeasureSensor;
import org.sonar.xoo.lang.MeasureSensor;
+import org.sonar.xoo.lang.SignificantCodeSensor;
import org.sonar.xoo.lang.SymbolReferencesSensor;
import org.sonar.xoo.lang.SyntaxHighlightingSensor;
import org.sonar.xoo.lang.XooCpdMapping;
@@ -173,7 +174,9 @@ public class XooPlugin implements Plugin {
context.addExtension(GlobalSensor.class);
}
if (context.getSonarQubeVersion().isGreaterThanOrEqual(Version.create(7, 2))) {
- context.addExtension(OneExternalIssuePerLineSensor.class);
+ context.addExtensions(
+ OneExternalIssuePerLineSensor.class,
+ SignificantCodeSensor.class);
}
}
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SignificantCodeSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SignificantCodeSensor.java
new file mode 100644
index 00000000000..108d232cb5a
--- /dev/null
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SignificantCodeSensor.java
@@ -0,0 +1,94 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.xoo.lang;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.apache.commons.lang.StringUtils;
+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.code.NewSignificantCode;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.xoo.Xoo;
+
+public class SignificantCodeSensor implements Sensor {
+ private static final Logger LOG = Loggers.get(SignificantCodeSensor.class);
+ private static final String FILE_EXTENSION = ".significantCode";
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name("Xoo Significant Code Ranges Sensor")
+ .onlyOnLanguages(Xoo.KEY);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ for (InputFile file : context.fileSystem().inputFiles(context.fileSystem().predicates().hasLanguages(Xoo.KEY))) {
+ processSignificantCodeFile(file, context);
+ }
+ }
+
+ private void processSignificantCodeFile(InputFile inputFile, SensorContext context) {
+ Path ioFile = inputFile.path();
+ Path significantCodeFile = ioFile.resolveSibling(ioFile.getFileName() + FILE_EXTENSION).toAbsolutePath();
+ if (Files.exists(significantCodeFile) && Files.isRegularFile(significantCodeFile)) {
+ LOG.debug("Processing " + significantCodeFile.toString());
+ try {
+ List<String> lines = Files.readAllLines(significantCodeFile, context.fileSystem().encoding());
+ NewSignificantCode significantCode = context.newSignificantCode()
+ .onFile(inputFile);
+ for (String line : lines) {
+ if (StringUtils.isBlank(line) || line.startsWith("#")) {
+ continue;
+ }
+ processLine(line, inputFile, significantCode);
+ }
+ significantCode.save();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+
+ private void processLine(String fileLine, InputFile inputFile, NewSignificantCode significantCode) {
+ String[] textPointer = fileLine.split(",");
+ if (textPointer.length != 3) {
+ throw new IllegalStateException("Invalid format in error file");
+ }
+
+ try {
+ int line = Integer.parseInt(textPointer[0]);
+ int startLineOffset = Integer.parseInt(textPointer[1]);
+ int endLineOffset = Integer.parseInt(textPointer[2]);
+
+ significantCode.addRange(inputFile.newRange(line, startLineOffset, line, endLineOffset));
+
+ } catch (NumberFormatException e) {
+ throw new IllegalStateException("Invalid format in error file", e);
+ }
+ }
+
+}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
index 9be43f0b4f3..dacd5b3f575 100644
--- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
+++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
@@ -62,6 +62,6 @@ public class XooPluginTest {
SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("7.2"), SonarQubeSide.SCANNER);
Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build();
new XooPlugin().define(context);
- assertThat(context.getExtensions()).hasSize(53).contains(OneExternalIssuePerLineSensor.class);
+ assertThat(context.getExtensions()).hasSize(54).contains(OneExternalIssuePerLineSensor.class);
}
}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SignificantCodeSensorTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SignificantCodeSensorTest.java
new file mode 100644
index 00000000000..115c1c030a2
--- /dev/null
+++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/lang/SignificantCodeSensorTest.java
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.xoo.lang;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultTextPointer;
+import org.sonar.api.batch.fs.internal.DefaultTextRange;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SignificantCodeSensorTest {
+ private SignificantCodeSensor sensor;
+ private SensorContextTester context;
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private File baseDir;
+
+ private InputFile inputFile;
+
+ @Before
+ public void prepare() throws IOException {
+ baseDir = temp.newFolder();
+ sensor = new SignificantCodeSensor();
+ context = SensorContextTester.create(baseDir);
+ inputFile = new TestInputFileBuilder("foo", baseDir, new File(baseDir, "src/foo.xoo"))
+ .setLanguage("xoo")
+ .setContents("some lines\nof code\nyet another line")
+ .build();
+ }
+
+ @Test
+ public void testDescriptor() {
+ sensor.describe(new DefaultSensorDescriptor());
+ }
+
+ @Test
+ public void testNoExceptionIfNoFileWithOffsets() {
+ context.fileSystem().add(inputFile);
+ sensor.execute(context);
+ }
+
+ @Test
+ public void testExecution() throws IOException {
+ File significantCode = new File(baseDir, "src/foo.xoo.significantCode");
+ FileUtils.write(significantCode, "1,1,4\n2,2,5");
+ context.fileSystem().add(inputFile);
+
+ sensor.execute(context);
+
+ assertThat(context.significantCodeTextRange("foo:src/foo.xoo", 1)).isEqualTo(range(1, 1, 4));
+ assertThat(context.significantCodeTextRange("foo:src/foo.xoo", 2)).isEqualTo(range(2, 2, 5));
+ }
+
+ private static TextRange range(int line, int startOffset, int endOffset) {
+ return new DefaultTextRange(new DefaultTextPointer(line, startOffset), new DefaultTextPointer(line, endOffset));
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesHashRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesHashRepository.java
index 60fc808f393..8c616407a4e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesHashRepository.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesHashRepository.java
@@ -31,7 +31,7 @@ public interface SourceLinesHashRepository {
List<String> getMatchingDB(Component component);
/**
- * The line processor will return line hashes taking into account significant code (if it was provided by a code analyzer).
+ * The line computer will compute line hashes taking into account significant code (if it was provided by a code analyzer).
* It will use a cached value, if possible. If it's generated, it's not cached since it's assumed that it won't be
* needed again after it is persisted.
*/
diff --git a/tests/projects/significantCode/sample-xoo/file.xoo b/tests/projects/significantCode/sample-xoo/file.xoo
new file mode 100644
index 00000000000..5c50979f1b4
--- /dev/null
+++ b/tests/projects/significantCode/sample-xoo/file.xoo
@@ -0,0 +1,11 @@
+000100******************************************************************00010000
+000200 IDENTIFICATION DIVISION. 00020000
+000300******************************************************************00030000
+000400 PROGRAM-ID. TEST-1. 00040000
+000500 AUTHOR. SONARSOURCE. 00050000
+000600 DATE-COMPILED. APRIL 2018. 00060000
+000700 (this line has no significant code) 00070009
+000800 00080000
+000900******************************************************************00090000
+001000* *00100000
+001100 MOVE WS-NB1 TO WS-NB2. 00110000 \ No newline at end of file
diff --git a/tests/projects/significantCode/sample-xoo/file_additional_line.xoo b/tests/projects/significantCode/sample-xoo/file_additional_line.xoo
new file mode 100644
index 00000000000..5f3293bbbe7
--- /dev/null
+++ b/tests/projects/significantCode/sample-xoo/file_additional_line.xoo
@@ -0,0 +1,12 @@
+000100******************************************************************00010000
+000200 IDENTIFICATION DIVISION. 00020000
+000300******************************************************************00030000
+000400 PROGRAM-ID. TEST-1. 00040000
+000500 AUTHOR. SONARSOURCE. 00050000
+000600 DATE-COMPILED. APRIL 2018. 00060000
+000700 (this line has no significant code) 00070009
+000800 00080000
+000800 THIS LINE WAS ADDED 00080000
+000900******************************************************************00090000
+001000* *00100000
+001100 MOVE WS-NB1 TO WS-NB2. 00110000 \ No newline at end of file
diff --git a/tests/projects/significantCode/sample-xoo/file_changed.xoo b/tests/projects/significantCode/sample-xoo/file_changed.xoo
new file mode 100644
index 00000000000..aeb1f6f5e42
--- /dev/null
+++ b/tests/projects/significantCode/sample-xoo/file_changed.xoo
@@ -0,0 +1,11 @@
+000109******************************************************************00010000
+000290 IDENTIFICATION DIVISION. 00020000
+000900******************************************************************00030000
+009400 PROGRAM-ID. TEST-1. - line changed 00040000
+090500 AUTHOR. SONARSOURCE. 00050000
+900600 DATE-COMPILED. APRIL 2018. 00060000
+000700 (this line has no significant code) - line changed 00070009
+000800 00080090
+000900******************************************************************00090900
+001000* *00109000
+001100 MOVE WS-NB1 TO WS-NB2. 00190000 \ No newline at end of file
diff --git a/tests/src/test/java/org/sonarqube/tests/source/SignificantCodeTest.java b/tests/src/test/java/org/sonarqube/tests/source/SignificantCodeTest.java
new file mode 100644
index 00000000000..833e57e7910
--- /dev/null
+++ b/tests/src/test/java/org/sonarqube/tests/source/SignificantCodeTest.java
@@ -0,0 +1,223 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.sonarqube.tests.source;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Issues.Issue;
+import org.sonarqube.ws.Issues.SearchWsResponse;
+import org.sonarqube.ws.client.issues.SearchRequest;
+import org.sonarqube.ws.client.sources.HashRequest;
+import util.ItUtils;
+
+import static org.apache.commons.io.FileUtils.copyDirectory;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonarqube.tests.source.SourceSuite.ORCHESTRATOR;
+import static util.ItUtils.projectDir;
+
+public class SignificantCodeTest {
+ private final String PROJECT_DIRECTORY = "sample-xoo";
+ private final String PROJECT_NAME = "sample-xoo";
+ private final String FILE_BASE = "file.xoo";
+ private final String FILE_CHANGED = "file_changed.xoo";
+ private final String FILE_ADDITIONAL_LINE = "file_additional_line.xoo";
+ private final String FILE_TO_ANALYSE = PROJECT_NAME + ":" + FILE_BASE;
+
+ @ClassRule
+ public static Orchestrator orchestrator = ORCHESTRATOR;
+
+ private SourceScmWS ws;
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Rule
+ public Tester tester = new Tester(orchestrator);
+
+ @Before
+ public void setUp() {
+ ORCHESTRATOR.resetData();
+ ws = new SourceScmWS(tester);
+ }
+
+ @Test
+ public void changes_in_margins_should_not_change_line_date() throws ParseException, IOException {
+ Path projectDirectory = disposableWorkspaceFor(PROJECT_DIRECTORY);
+ deployFileWithsignificantCodeRanges(projectDirectory, 7);
+
+ // 1st run
+ SonarScanner scanner = SonarScanner.create(projectDirectory.toFile())
+ .setProjectKey(PROJECT_NAME)
+ .setSourceDirs(FILE_BASE);
+ orchestrator.executeBuild(scanner);
+ Map<Integer, LineData> scmData1 = ws.getScmData(FILE_TO_ANALYSE);
+
+ // 2nd run
+ deployChangedFile(projectDirectory, FILE_CHANGED);
+
+ scanner = SonarScanner.create(projectDirectory.toFile())
+ .setProjectKey(PROJECT_NAME)
+ .setSourceDirs(FILE_BASE);
+ orchestrator.executeBuild(scanner);
+
+ // Check that only line 4 is considered as changed
+ Map<Integer, LineData> scmData2 = ws.getScmData(FILE_TO_ANALYSE);
+
+ for (Map.Entry<Integer, LineData> e : scmData2.entrySet()) {
+ if (e.getKey() == 4) {
+ assertThat(e.getValue().date).isAfter(scmData1.get(1).date);
+ } else {
+ assertThat(e.getValue().date).isEqualTo(scmData1.get(1).date);
+ }
+ }
+ }
+
+ @Test
+ public void migration_should_not_affect_unchanged_lines() throws IOException, ParseException {
+ Path projectDirectory = disposableWorkspaceFor(PROJECT_DIRECTORY);
+
+ // 1st run
+ SonarScanner scanner = SonarScanner.create(projectDirectory.toFile())
+ .setProjectKey(PROJECT_NAME)
+ .setSourceDirs(FILE_BASE);
+ orchestrator.executeBuild(scanner);
+ Map<Integer, LineData> scmData1 = ws.getScmData(FILE_TO_ANALYSE);
+ String[] lineHashes1 = getLineHashes();
+
+ // 2nd run
+ deployFileWithsignificantCodeRanges(projectDirectory, 7);
+ scanner = SonarScanner.create(projectDirectory.toFile())
+ .setProjectKey(PROJECT_NAME)
+ .setSourceDirs(FILE_BASE);
+ orchestrator.executeBuild(scanner);
+
+ // Check that no line was modified
+ Map<Integer, LineData> scmData2 = ws.getScmData(FILE_TO_ANALYSE);
+
+ for (Map.Entry<Integer, LineData> e : scmData2.entrySet()) {
+ assertThat(e.getValue().date).isEqualTo(scmData1.get(1).date);
+ }
+
+ // Check that line hashes changed for all lines, even though the file didn't change
+ String[] lineHashes2 = getLineHashes();
+
+ for (int i = 0; i < lineHashes2.length; i++) {
+ assertThat(lineHashes2[i]).isNotEqualTo(lineHashes1[i]);
+ }
+ }
+
+ @Test
+ public void issue_tracking() throws Exception {
+ ORCHESTRATOR.getServer().provisionProject(PROJECT_NAME, PROJECT_NAME);
+ ItUtils.restoreProfile(ORCHESTRATOR, getClass().getResource("/one-xoo-issue-per-line.xml"));
+ ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_NAME, "xoo", "one-xoo-issue-per-line");
+
+ Path projectDirectory = disposableWorkspaceFor(PROJECT_DIRECTORY);
+
+ // 1st run
+ deployFileWithsignificantCodeRanges(projectDirectory, 7);
+ SonarScanner scanner = SonarScanner.create(projectDirectory.toFile())
+ .setProjectKey(PROJECT_NAME)
+ .setSourceDirs(FILE_BASE);
+ orchestrator.executeBuild(scanner);
+
+ Map<Integer, Issue> issues1 = getIssues().stream().collect(Collectors.toMap(i -> i.getLine(), i -> i));
+
+ // 2nd run
+ deployChangedFile(projectDirectory, FILE_ADDITIONAL_LINE);
+ deployFileWithsignificantCodeRanges(projectDirectory, 7);
+
+ scanner = SonarScanner.create(projectDirectory.toFile())
+ .setProjectKey(PROJECT_NAME)
+ .setSourceDirs(FILE_BASE);
+ orchestrator.executeBuild(scanner);
+
+ List<Issue> issues2 = getIssues();
+
+ // Check that all issues were tracking except the issue on the new line
+ assertThat(issues1.size()).isEqualTo(11);
+ assertThat(issues2.size()).isEqualTo(12);
+
+ for (Issue issue : issues2) {
+ if (issue.getLine() < 9) {
+ assertThat(issue.getKey()).isEqualTo(issues1.get(issue.getLine()).getKey());
+ } else if (issue.getLine() == 9) {
+ // this is the new issue
+ List<String> keys = issues1.values().stream().map(i -> i.getKey()).collect(Collectors.toList());
+ assertThat(issue.getKey()).isNotIn(keys);
+ } else {
+ assertThat(issue.getKey()).isEqualTo(issues1.get(issue.getLine() - 1).getKey());
+ }
+ }
+
+ }
+
+ private String[] getLineHashes() {
+ String hashes = tester.wsClient().sources().hash(new HashRequest().setKey(FILE_TO_ANALYSE));
+ return StringUtils.split(hashes, "\n");
+ }
+
+ private List<Issue> getIssues() {
+ SearchWsResponse response = tester.wsClient().issues().search(new SearchRequest().setComponentKeys(Collections.singletonList(FILE_TO_ANALYSE)));
+ return response.getIssuesList();
+ }
+
+ private void deployFileWithsignificantCodeRanges(Path projectBaseDir, int lineWithoutSignificantCode) throws IOException {
+ Path file = projectBaseDir.resolve("file.xoo");
+ Path significantLineRangesFile = projectBaseDir.resolve("file.xoo.significantCode");
+
+ int numLines = Files.readAllLines(file).size();
+ List<String> lines = IntStream.rangeClosed(1, numLines)
+ .mapToObj(i -> i == lineWithoutSignificantCode ? "" : i + ",6,72")
+ .collect(Collectors.toList());
+
+ Files.write(significantLineRangesFile, lines);
+ }
+
+ private void deployChangedFile(Path projectBaseDir, String fileName) throws IOException {
+ Path file = projectBaseDir.resolve(FILE_BASE);
+ Path file_changed = projectBaseDir.resolve(fileName);
+ Files.move(file_changed, file, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ private Path disposableWorkspaceFor(String project) throws IOException {
+ File origin = projectDir("significantCode/" + project);
+ copyDirectory(origin.getParentFile(), temporaryFolder.getRoot());
+ return temporaryFolder.getRoot().toPath().resolve(project);
+ }
+}