diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2018-10-22 10:06:52 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-11-07 20:21:03 +0100 |
commit | 7f59b2e51acb56b9d280fbeea4564194f3dd07cd (patch) | |
tree | c4ecd37498f71cf84ec1c0a29b341750448070dc /server/sonar-server/src | |
parent | c7153cbc9057fa19651dba01c8d9c94ff5e1f4b2 (diff) | |
download | sonarqube-7f59b2e51acb56b9d280fbeea4564194f3dd07cd.tar.gz sonarqube-7f59b2e51acb56b9d280fbeea4564194f3dd07cd.zip |
SONAR-11394 Calculate estimated overall duplication in SLB
Diffstat (limited to 'server/sonar-server/src')
5 files changed, 347 insertions, 185 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/Duplication.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/Duplication.java new file mode 100644 index 00000000000..88c38fdc548 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/Duplication.java @@ -0,0 +1,77 @@ +/* + * 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.server.duplication.ws; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.db.component.ComponentDto; + +public class Duplication { + private final ComponentDto componentDto; + private final String componentDbKey; + private final Integer from; + private final Integer size; + private final boolean removed; + + static Duplication newRemovedComponent(String componentDbKey, Integer from, Integer size) { + return new Duplication(null, componentDbKey, from, size, true); + } + + static Duplication newTextComponent(String componentDbKey, Integer from, Integer size) { + return new Duplication(null, componentDbKey, from, size, false); + } + + static Duplication newComponent(ComponentDto componentDto, Integer from, Integer size) { + return new Duplication(componentDto, componentDto.getDbKey(), from, size, false); + } + + private Duplication(@Nullable ComponentDto componentDto, String componentDbKey, Integer from, Integer size, boolean removed) { + this.componentDto = componentDto; + this.componentDbKey = componentDbKey; + this.from = from; + this.size = size; + this.removed = removed; + } + + String componentDbKey() { + return componentDbKey; + } + + Integer from() { + return from; + } + + Integer size() { + return size; + } + + public boolean removed() { + return removed; + } + + /** + * can be null if the file wasn't found in DB. This can happen if the target was removed (cross-project duplications) or + * if the target refers to an unchanged file in SLBs/PRs. + */ + @CheckForNull + public ComponentDto componentDto() { + return componentDto; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java index 8482626bb25..f6cc09f3196 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java +++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java @@ -22,11 +22,11 @@ package org.sonar.server.duplication.ws; import com.google.common.annotations.VisibleForTesting; import java.io.Serializable; import java.io.StringReader; -import java.util.Collections; +import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.xml.stream.XMLInputFactory; @@ -40,12 +40,9 @@ import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDao; import org.sonar.db.component.ComponentDto; -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Maps.newHashMap; - @ServerSide public class DuplicationsParser { - + private static final BlockComparator BLOCK_COMPARATOR = new BlockComparator(); private final ComponentDao componentDao; public DuplicationsParser(ComponentDao componentDao) { @@ -53,53 +50,76 @@ public class DuplicationsParser { } public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String pullRequest, @Nullable String duplicationsData) { - Map<String, ComponentDto> componentsByKey = newHashMap(); - List<Block> blocks = newArrayList(); - if (duplicationsData != null) { - try { - SMInputFactory inputFactory = initStax(); - SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(duplicationsData)); - root.advance(); // <duplications> - SMInputCursor cursor = root.childElementCursor("g"); - while (cursor.getNext() != null) { - List<Duplication> duplications = newArrayList(); - SMInputCursor bCursor = cursor.childElementCursor("b"); - while (bCursor.getNext() != null) { - String from = bCursor.getAttrValue("s"); - String size = bCursor.getAttrValue("l"); - String componentKey = bCursor.getAttrValue("r"); - if (from != null && size != null && componentKey != null) { - duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentKey, session)); + Map<String, ComponentDto> componentsByKey = new HashMap<>(); + List<Block> blocks = new ArrayList<>(); + if (duplicationsData == null) { + return blocks; + } + + DuplicationComparator duplicationComparator = new DuplicationComparator(component.uuid(), component.projectUuid()); + + try { + SMInputFactory inputFactory = initStax(); + SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(duplicationsData)); + root.advance(); // <duplications> + SMInputCursor cursor = root.childElementCursor("g"); + while (cursor.getNext() != null) { + List<Duplication> duplications = new ArrayList<>(); + SMInputCursor bCursor = cursor.childElementCursor("b"); + while (bCursor.getNext() != null) { + String from = bCursor.getAttrValue("s"); + String size = bCursor.getAttrValue("l"); + boolean onlyText = Boolean.parseBoolean(bCursor.getAttrValue("t")); + String componentDbKey = bCursor.getAttrValue("r"); + if (from != null && size != null && componentDbKey != null) { + if (onlyText) { + // flag means that the target refers to an unchanged file in SLBs/PRs that doesn't exist in DB. + // Display as text without a link or other details. + duplications.add(Duplication.newTextComponent(componentDbKey, Integer.valueOf(from), Integer.valueOf(size))); + } else { + duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentDbKey, session)); } } - Collections.sort(duplications, new DuplicationComparator(component.uuid(), component.projectUuid())); - blocks.add(new Block(duplications)); } - Collections.sort(blocks, new BlockComparator()); - } catch (XMLStreamException e) { - throw new IllegalStateException("XML is not valid", e); + duplications.sort(duplicationComparator); + blocks.add(new Block(duplications)); } + blocks.sort(BLOCK_COMPARATOR); + return blocks; + } catch (XMLStreamException e) { + throw new IllegalStateException("XML is not valid", e); } - return blocks; } - private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from, String size, - String componentDbKey, DbSession session) { + private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from, + String size, String componentDbKey, DbSession session) { String componentKey = convertToKey(componentDbKey); - ComponentDto component = componentsByKey.get(componentKey); - if (component == null) { - Optional<ComponentDto> componentDtoOptional; - if (branch != null) { - componentDtoOptional = componentDao.selectByKeyAndBranch(session, componentKey, branch); - } else if (pullRequest != null) { - componentDtoOptional = componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest); - } else { - componentDtoOptional = componentDao.selectByKey(session, componentKey); - } - component = componentDtoOptional.isPresent() ? componentDtoOptional.get() : null; + + ComponentDto component; + if (componentsByKey.containsKey(componentKey)) { + component = componentsByKey.get(componentKey); + } else { + component = loadComponent(session, componentKey, branch, pullRequest); componentsByKey.put(componentKey, component); } - return new Duplication(component, Integer.valueOf(from), Integer.valueOf(size)); + + if (component != null) { + return Duplication.newComponent(component, Integer.valueOf(from), Integer.valueOf(size)); + } else { + //This can happen if the target was removed (cross-project duplications) + return Duplication.newRemovedComponent(componentKey, Integer.valueOf(from), Integer.valueOf(size)); + } + } + + @CheckForNull + private ComponentDto loadComponent(DbSession session, String componentKey, @Nullable String branch, @Nullable String pullRequest) { + if (branch != null) { + return componentDao.selectByKeyAndBranch(session, componentKey, branch).orElse(null); + } else if (pullRequest != null) { + return componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest).orElse(null); + } else { + return componentDao.selectByKey(session, componentKey).orElse(null); + } } private static String convertToKey(String dbKey) { @@ -116,15 +136,21 @@ public class DuplicationsParser { return new SMInputFactory(xmlFactory); } + /** + * Sorts the duplications with the following criteria: + * - Duplications in the same file by starting line + * - Duplications in the same project + * - Cross project duplications + */ @VisibleForTesting static class DuplicationComparator implements Comparator<Duplication>, Serializable { private static final long serialVersionUID = 1; - private final String uuid; + private final String fileUuid; private final String projectUuid; - DuplicationComparator(String uuid, String projectUuid) { - this.uuid = uuid; + DuplicationComparator(String fileUuid, String projectUuid) { + this.fileUuid = fileUuid; this.projectUuid = projectUuid; } @@ -133,34 +159,40 @@ public class DuplicationsParser { if (d1 == null || d2 == null) { return -1; } - ComponentDto file1 = d1.file(); - ComponentDto file2 = d2.file(); + ComponentDto file1 = d1.componentDto(); + ComponentDto file2 = d2.componentDto(); - if (file1 == null || file2 == null) { - return -1; - } - if (file1.equals(d2.file())) { + if (file1 != null && file1.equals(file2)) { // if duplication on same file => order by starting line return d1.from().compareTo(d2.from()); } - if (file1.uuid().equals(uuid)) { + if (sameFile(file1) && !sameFile(file2)) { // the current resource must be displayed first return -1; } - if (file2.uuid().equals(uuid)) { + if (sameFile(file2) && !sameFile(file1)) { // the current resource must be displayed first return 1; } - if (StringUtils.equals(file1.projectUuid(), projectUuid) && !StringUtils.equals(file2.projectUuid(), projectUuid)) { + if (sameProject(file1) && !sameProject(file2)) { // if resource is in the same project, this it must be displayed first return -1; } - if (StringUtils.equals(file2.projectUuid(), projectUuid) && !StringUtils.equals(file1.projectUuid(), projectUuid)) { + if (sameProject(file2) && !sameProject(file1)) { // if resource is in the same project, this it must be displayed first return 1; } + return d1.from().compareTo(d2.from()); } + + private boolean sameFile(@Nullable ComponentDto otherDto) { + return otherDto != null && StringUtils.equals(otherDto.uuid(), fileUuid); + } + + private boolean sameProject(@Nullable ComponentDto otherDto) { + return otherDto == null || StringUtils.equals(otherDto.projectUuid(), projectUuid); + } } private static class BlockComparator implements Comparator<Block>, Serializable { @@ -180,34 +212,6 @@ public class DuplicationsParser { } } - public static class Duplication { - private final ComponentDto file; - private final Integer from; - private final Integer size; - - Duplication(@Nullable ComponentDto file, Integer from, Integer size) { - this.file = file; - this.from = from; - this.size = size; - } - - /** - * File can be null when duplication is linked on a removed file - */ - @CheckForNull - ComponentDto file() { - return file; - } - - Integer from() { - return from; - } - - Integer size() { - return size; - } - } - static class Block { private final List<Duplication> duplications; diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java index 7f792ca70d7..4212ca7be74 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java @@ -20,10 +20,13 @@ package org.sonar.server.duplication.ws; import com.google.common.annotations.VisibleForTesting; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDao; @@ -32,62 +35,87 @@ import org.sonarqube.ws.Duplications; import org.sonarqube.ws.Duplications.Block; import org.sonarqube.ws.Duplications.ShowResponse; -import static com.google.common.collect.Maps.newHashMap; import static org.sonar.core.util.Protobuf.setNullable; -// TODO Add UT on branch public class ShowResponseBuilder { private final ComponentDao componentDao; + private final Map<String, Reference> refByComponentKey = new HashMap<>(); public ShowResponseBuilder(DbClient dbClient) { this.componentDao = dbClient.componentDao(); } - @VisibleForTesting - ShowResponseBuilder(ComponentDao componentDao) { + @VisibleForTesting ShowResponseBuilder(ComponentDao componentDao) { this.componentDao = componentDao; } ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest) { ShowResponse.Builder response = ShowResponse.newBuilder(); - Map<String, String> refByComponentKey = newHashMap(); blocks.stream() - .map(block -> toWsDuplication(block, refByComponentKey)) + .map(this::toWsDuplication) .forEach(response::addDuplications); - writeFiles(session, response, refByComponentKey, branch, pullRequest); - + writeFileRefs(session, response, branch, pullRequest); return response.build(); } - private static Duplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block, Map<String, String> refByComponentKey) { + private Duplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block) { Duplications.Duplication.Builder wsDuplication = Duplications.Duplication.newBuilder(); block.getDuplications().stream() - .map(d -> toWsBlock(refByComponentKey, d)) + .map(this::toWsBlock) .forEach(wsDuplication::addBlocks); return wsDuplication; } - private static Block.Builder toWsBlock(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication) { - String ref = null; - ComponentDto componentDto = duplication.file(); - if (componentDto != null) { - String componentKey = componentDto.getDbKey(); - ref = refByComponentKey.computeIfAbsent(componentKey, k -> Integer.toString(refByComponentKey.size() + 1)); + private Block.Builder toWsBlock(Duplication duplication) { + Block.Builder block = Block.newBuilder(); + + if (!duplication.removed()) { + Reference ref = refByComponentKey.computeIfAbsent(duplication.componentDbKey(), k -> new Reference( + Integer.toString(refByComponentKey.size() + 1), + duplication.componentDto(), + duplication.componentDbKey())); + block.setRef(ref.id); } - Block.Builder block = Block.newBuilder(); block.setFrom(duplication.from()); block.setSize(duplication.size()); - setNullable(ref, block::setRef); return block; } - private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch, - @Nullable String pullRequest) { + private void writeFileRefs(DbSession session, ShowResponse.Builder response, @Nullable String branch, @Nullable String pullRequest) { + Map<String, ComponentDto> projectsByUuid = new HashMap<>(); + Map<String, ComponentDto> parentModulesByUuid = new HashMap<>(); + + for (Map.Entry<String, Reference> entry : refByComponentKey.entrySet()) { + Reference ref = entry.getValue(); + ComponentDto file = ref.dto(); + + if (file != null) { + ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session); + ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session); + response.putFiles(ref.id(), toWsFile(file, project, parentModule, branch, pullRequest)); + } else { + response.putFiles(ref.id(), toWsFile(ref.componentKey(), branch, pullRequest)); + } + } + } + + private static Duplications.File toWsFile(String componentKey, @Nullable String branch, @Nullable String pullRequest) { + Duplications.File.Builder wsFile = Duplications.File.newBuilder(); + String keyWithoutBranch = ComponentDto.removeBranchAndPullRequestFromKey(componentKey); + wsFile.setKey(keyWithoutBranch); + wsFile.setName(StringUtils.substringAfterLast(keyWithoutBranch, ":")); + setNullable(branch, wsFile::setBranch); + setNullable(pullRequest, wsFile::setPullRequest); + return wsFile.build(); + } + + private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, + @Nullable String branch, @Nullable String pullRequest) { Duplications.File.Builder wsFile = Duplications.File.newBuilder(); wsFile.setKey(file.getKey()); wsFile.setUuid(file.uuid()); @@ -110,25 +138,6 @@ public class ShowResponseBuilder { return wsFile.build(); } - private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch, @Nullable String pullRequest) { - Map<String, ComponentDto> projectsByUuid = newHashMap(); - Map<String, ComponentDto> parentModulesByUuid = newHashMap(); - Map<String, Duplications.File> filesByRef = response.getMutableFiles(); - - for (Map.Entry<String, String> entry : refByComponentKey.entrySet()) { - String componentKey = entry.getKey(); - String ref = entry.getValue(); - Optional<ComponentDto> fileOptional = componentDao.selectByKey(session, componentKey); - if (fileOptional.isPresent()) { - ComponentDto file = fileOptional.get(); - - ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session); - ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session); - filesByRef.put(ref, toWsFile(file, project, parentModule, branch, pullRequest)); - } - } - } - private ComponentDto getProject(String projectUuid, Map<String, ComponentDto> projectsByUuid, DbSession session) { ComponentDto project = projectsByUuid.get(projectUuid); if (project == null) { @@ -153,4 +162,30 @@ public class ShowResponseBuilder { return project; } + private static class Reference { + private final String id; + private final ComponentDto dto; + private final String componentKey; + + public Reference(String id, @Nullable ComponentDto dto, String componentKey) { + this.id = id; + this.dto = dto; + this.componentKey = componentKey; + } + + public String id() { + return id; + } + + @CheckForNull + public ComponentDto dto() { + return dto; + } + + public String componentKey() { + return componentKey; + } + + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java index d45ce9934fc..bb13f39ff2d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java @@ -37,7 +37,7 @@ public class DuplicationsParserTest { @Rule public DbTester db = DbTester.create(); - DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao()); + private DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao()); @Test public void empty_list_when_no_data() { @@ -60,17 +60,17 @@ public class DuplicationsParserTest { "</duplications>", file.getDbKey(), file.getDbKey())); assertThat(blocks).hasSize(1); - List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications(); + List<Duplication> duplications = blocks.get(0).getDuplications(); assertThat(duplications).hasSize(2); // Smallest line comes first - DuplicationsParser.Duplication duplication1 = duplications.get(0); - assertThat(duplication1.file()).isEqualTo(file); + Duplication duplication1 = duplications.get(0); + assertThat(duplication1.componentDto()).isEqualTo(file); assertThat(duplication1.from()).isEqualTo(20); assertThat(duplication1.size()).isEqualTo(5); - DuplicationsParser.Duplication duplication2 = duplications.get(1); - assertThat(duplication2.file()).isEqualTo(file); + Duplication duplication2 = duplications.get(1); + assertThat(duplication2.componentDto()).isEqualTo(file); assertThat(duplication2.from()).isEqualTo(31); assertThat(duplication2.size()).isEqualTo(5); } @@ -89,17 +89,17 @@ public class DuplicationsParserTest { "</duplications>", file2.getDbKey(), file1.getDbKey())); assertThat(blocks).hasSize(1); - List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications(); + List<Duplication> duplications = blocks.get(0).getDuplications(); assertThat(duplications).hasSize(2); // Current file comes first - DuplicationsParser.Duplication duplication1 = duplications.get(0); - assertThat(duplication1.file()).isEqualTo(file1); + Duplication duplication1 = duplications.get(0); + assertThat(duplication1.componentDto()).isEqualTo(file1); assertThat(duplication1.from()).isEqualTo(31); assertThat(duplication1.size()).isEqualTo(5); - DuplicationsParser.Duplication duplication2 = duplications.get(1); - assertThat(duplication2.file()).isEqualTo(file2); + Duplication duplication2 = duplications.get(1); + assertThat(duplication2.componentDto()).isEqualTo(file2); assertThat(duplication2.from()).isEqualTo(20); assertThat(duplication2.size()).isEqualTo(5); } @@ -121,25 +121,25 @@ public class DuplicationsParserTest { "</duplications>", file1.getDbKey(), fileOnProject2.getDbKey(), file2.getDbKey())); assertThat(blocks).hasSize(1); - List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications(); + List<Duplication> duplications = blocks.get(0).getDuplications(); assertThat(duplications).hasSize(3); // Current file's project comes first - DuplicationsParser.Duplication duplication1 = duplications.get(0); - assertThat(duplication1.file()).isEqualTo(file1); + Duplication duplication1 = duplications.get(0); + assertThat(duplication1.componentDto()).isEqualTo(file1); assertThat(duplication1.from()).isEqualTo(148); assertThat(duplication1.size()).isEqualTo(24); - DuplicationsParser.Duplication duplication2 = duplications.get(1); - assertThat(duplication2.file()).isEqualTo(file2); + Duplication duplication2 = duplications.get(1); + assertThat(duplication2.componentDto()).isEqualTo(file2); assertThat(duplication2.from()).isEqualTo(111); assertThat(duplication2.size()).isEqualTo(24); // Other project comes last - DuplicationsParser.Duplication duplication3 = duplications.get(2); - assertThat(duplication3.file()).isEqualTo(fileOnProject2); + Duplication duplication3 = duplications.get(2); + assertThat(duplication3.componentDto()).isEqualTo(fileOnProject2); assertThat(duplication3.from()).isEqualTo(137); assertThat(duplication3.size()).isEqualTo(24); } @@ -189,17 +189,17 @@ public class DuplicationsParserTest { "</duplications>", file.getDbKey(), "not_existing")); assertThat(blocks).hasSize(1); - List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications(); + List<Duplication> duplications = blocks.get(0).getDuplications(); assertThat(duplications).hasSize(2); // Duplications on removed file - DuplicationsParser.Duplication duplication1 = duplication(duplications, null); - assertThat(duplication1.file()).isNull(); + Duplication duplication1 = duplication(duplications, null); + assertThat(duplication1.componentDto()).isNull(); assertThat(duplication1.from()).isEqualTo(31); assertThat(duplication1.size()).isEqualTo(5); - DuplicationsParser.Duplication duplication2 = duplication(duplications, file.getDbKey()); - assertThat(duplication2.file()).isEqualTo(file); + Duplication duplication2 = duplication(duplications, file.getDbKey()); + assertThat(duplication2.componentDto()).isEqualTo(file); assertThat(duplication2.from()).isEqualTo(20); assertThat(duplication2.size()).isEqualTo(5); } @@ -215,26 +215,33 @@ public class DuplicationsParserTest { DuplicationsParser.DuplicationComparator comparator = new DuplicationsParser.DuplicationComparator(currentFile.uuid(), currentFile.projectUuid()); // On same file - assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(-1); + assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2), + Duplication.newComponent(currentFile, 5, 2))).isEqualTo(-1); // Different files on same project - assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(fileOnSameProject, 5, 2))).isEqualTo(-1); - assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(1); + assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2), + Duplication.newComponent(fileOnSameProject, 5, 2))).isEqualTo(-1); + assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), + Duplication.newComponent(currentFile, 5, 2))).isEqualTo(1); // Different files on different projects - assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 5, 2), new DuplicationsParser.Duplication(fileOnDifferentProject, 2, 2))).isEqualTo(-1); - assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnDifferentProject, 5, 2), new DuplicationsParser.Duplication(fileOnSameProject, 2, 2))).isEqualTo(1); + assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 5, 2), + Duplication.newComponent(fileOnDifferentProject, 2, 2))).isEqualTo(-1); + assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2), + Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(1); // Files on 2 different projects ComponentDto project3 = db.components().insertPrivateProject(); - assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnDifferentProject, 5, 2), - new DuplicationsParser.Duplication(project3, 2, 2))).isEqualTo(1); + assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2), + Duplication.newComponent(project3, 2, 2))).isEqualTo(1); // With null duplications - assertThat(comparator.compare(null, new DuplicationsParser.Duplication(fileOnSameProject, 2, 2))).isEqualTo(-1); - assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 2, 2), null)).isEqualTo(-1); + assertThat(comparator.compare(null, Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(-1); + assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), null)).isEqualTo(-1); assertThat(comparator.compare(null, null)).isEqualTo(-1); // On some removed file - assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(null, 5, 2))).isEqualTo(-1); - assertThat(comparator.compare(new DuplicationsParser.Duplication(null, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(-1); + assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2), + Duplication.newRemovedComponent("key1", 5, 2))).isEqualTo(-1); + assertThat(comparator.compare(Duplication.newRemovedComponent("key2", 2, 2), + Duplication.newComponent(currentFile, 5, 2))).isEqualTo(1); } @Test @@ -252,19 +259,19 @@ public class DuplicationsParserTest { "</duplications>", file2.getDbKey(), file1.getDbKey())); assertThat(blocks).hasSize(1); - List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications(); + List<Duplication> duplications = blocks.get(0).getDuplications(); assertThat(duplications).hasSize(2); // Current file comes first - DuplicationsParser.Duplication duplication1 = duplications.get(0); - assertThat(duplication1.file()).isEqualTo(file1); - assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey()); + Duplication duplication1 = duplications.get(0); + assertThat(duplication1.componentDto()).isEqualTo(file1); + assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey()); assertThat(duplication1.from()).isEqualTo(31); assertThat(duplication1.size()).isEqualTo(5); - DuplicationsParser.Duplication duplication2 = duplications.get(1); - assertThat(duplication2.file()).isEqualTo(file2); - assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey()); + Duplication duplication2 = duplications.get(1); + assertThat(duplication2.componentDto()).isEqualTo(file2); + assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey()); assertThat(duplication2.from()).isEqualTo(20); assertThat(duplication2.size()).isEqualTo(5); } @@ -284,26 +291,26 @@ public class DuplicationsParserTest { "</duplications>", file2.getDbKey(), file1.getDbKey())); assertThat(blocks).hasSize(1); - List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications(); + List<Duplication> duplications = blocks.get(0).getDuplications(); assertThat(duplications).hasSize(2); // Current file comes first - DuplicationsParser.Duplication duplication1 = duplications.get(0); - assertThat(duplication1.file()).isEqualTo(file1); - assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey()); + Duplication duplication1 = duplications.get(0); + assertThat(duplication1.componentDto()).isEqualTo(file1); + assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey()); assertThat(duplication1.from()).isEqualTo(31); assertThat(duplication1.size()).isEqualTo(5); - DuplicationsParser.Duplication duplication2 = duplications.get(1); - assertThat(duplication2.file()).isEqualTo(file2); - assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey()); + Duplication duplication2 = duplications.get(1); + assertThat(duplication2.componentDto()).isEqualTo(file2); + assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey()); assertThat(duplication2.from()).isEqualTo(20); assertThat(duplication2.size()).isEqualTo(5); } - private static DuplicationsParser.Duplication duplication(List<DuplicationsParser.Duplication> duplications, @Nullable final String componentKey) { - return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.file() == null - : input.file() != null && componentKey.equals(input.file().getDbKey()))); + private static Duplication duplication(List<Duplication> duplications, @Nullable final String componentKey) { + return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.componentDto() == null + : input.componentDto() != null && componentKey.equals(input.componentDto().getDbKey()))); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java index 4042c185b33..082652ad33b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java @@ -51,8 +51,8 @@ public class ShowResponseBuilderTest { ComponentDto file2 = db.components().insertComponent(newFileDto(module)); List<DuplicationsParser.Block> blocks = newArrayList(); blocks.add(new DuplicationsParser.Block(newArrayList( - new DuplicationsParser.Duplication(file1, 57, 12), - new DuplicationsParser.Duplication(file2, 73, 12)))); + Duplication.newComponent(file1, 57, 12), + Duplication.newComponent(file2, 73, 12)))); test(blocks, null, null, "{\n" + @@ -96,8 +96,8 @@ public class ShowResponseBuilderTest { ComponentDto file2 = db.components().insertComponent(newFileDto(project)); List<DuplicationsParser.Block> blocks = newArrayList(); blocks.add(new DuplicationsParser.Block(newArrayList( - new DuplicationsParser.Duplication(file1, 57, 12), - new DuplicationsParser.Duplication(file2, 73, 12)))); + Duplication.newComponent(file1, 57, 12), + Duplication.newComponent(file2, 73, 12)))); test(blocks, null, null, "{\n" + @@ -136,9 +136,9 @@ public class ShowResponseBuilderTest { ComponentDto file = db.components().insertComponent(newFileDto(project)); List<DuplicationsParser.Block> blocks = newArrayList(); blocks.add(new DuplicationsParser.Block(newArrayList( - new DuplicationsParser.Duplication(file, 57, 12), + Duplication.newComponent(file, 57, 12), // Duplication on a removed file - new DuplicationsParser.Duplication(null, 73, 12)))); + Duplication.newRemovedComponent("key", 73, 12)))); test(blocks, null, null, "{\n" + @@ -166,6 +166,45 @@ public class ShowResponseBuilderTest { } @Test + public void write_duplications_with_a_component_without_details() { + ComponentDto project = db.components().insertPrivateProject(); + ComponentDto file = db.components().insertComponent(newFileDto(project)); + List<DuplicationsParser.Block> blocks = newArrayList(); + blocks.add(new DuplicationsParser.Block(newArrayList( + Duplication.newComponent(file, 57, 12), + // Duplication on a file without details + Duplication.newTextComponent("project:path/to/file", 73, 12)))); + + test(blocks, null, null, + "{\n" + + " \"duplications\": [\n" + + " {\n" + + " \"blocks\": [\n" + + " {\n" + + " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" + + " },\n" + + " {\n" + + " \"from\": 73, \"size\": 12\n" + + " }\n" + + " ]\n" + + " }," + + " ],\n" + + " \"files\": {\n" + + " \"1\": {\n" + + " \"key\": \"" + file.getKey() + "\",\n" + + " \"name\": \"" + file.longName() + "\",\n" + + " \"project\": \"" + project.getKey() + "\",\n" + + " \"projectName\": \"" + project.longName() + "\",\n" + + " }\n" + + " \"2\": {\n" + + " \"key\": \"project:path/to/file\",\n" + + " \"name\": \"path/to/file\",\n" + + " }\n" + + " }" + + "}"); + } + + @Test public void write_duplications_on_branch() { ComponentDto project = db.components().insertMainBranch(); ComponentDto branch = db.components().insertProjectBranch(project); @@ -173,8 +212,8 @@ public class ShowResponseBuilderTest { ComponentDto file2 = db.components().insertComponent(newFileDto(branch)); List<DuplicationsParser.Block> blocks = newArrayList(); blocks.add(new DuplicationsParser.Block(newArrayList( - new DuplicationsParser.Duplication(file1, 57, 12), - new DuplicationsParser.Duplication(file2, 73, 12)))); + Duplication.newComponent(file1, 57, 12), + Duplication.newComponent(file2, 73, 12)))); test(blocks, branch.getBranch(), null, "{\n" + @@ -217,8 +256,8 @@ public class ShowResponseBuilderTest { ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest)); List<DuplicationsParser.Block> blocks = newArrayList(); blocks.add(new DuplicationsParser.Block(newArrayList( - new DuplicationsParser.Duplication(file1, 57, 12), - new DuplicationsParser.Duplication(file2, 73, 12)))); + Duplication.newComponent(file1, 57, 12), + Duplication.newComponent(file2, 73, 12)))); test(blocks, null, pullRequest.getPullRequest(), "{\n" + @@ -255,7 +294,7 @@ public class ShowResponseBuilderTest { @Test public void write_nothing_when_no_data() { - test(Collections.emptyList(), null, null,"{\"duplications\": [], \"files\": {}}"); + test(Collections.emptyList(), null, null, "{\"duplications\": [], \"files\": {}}"); } private void test(List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest, String expected) { |