summaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java14
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java174
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/phases/FileIndexer.java25
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java128
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/phases/FileIndexerTest.java76
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewDirectory-result.xml2
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewLibrary-result.xml2
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys-result.xml65
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys.xml65
13 files changed, 494 insertions, 70 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
index afeec5e589b..10aa20e7872 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
@@ -91,14 +91,17 @@ public class DefaultIndex extends SonarIndex {
private final DeprecatedViolations deprecatedViolations;
private ModuleIssues moduleIssues;
+ private ResourceKeyMigration migration;
+
public DefaultIndex(PersistenceManager persistence, DefaultResourceCreationLock lock, ProjectTree projectTree, MetricFinder metricFinder,
- ScanGraph graph, DeprecatedViolations deprecatedViolations) {
+ ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration) {
this.persistence = persistence;
this.lock = lock;
this.projectTree = projectTree;
this.metricFinder = metricFinder;
this.graph = graph;
this.deprecatedViolations = deprecatedViolations;
+ this.migration = migration;
}
public void start() {
@@ -111,6 +114,7 @@ public class DefaultIndex extends SonarIndex {
void doStart(Project rootProject) {
Bucket bucket = new Bucket(rootProject);
buckets.put(rootProject, bucket);
+ migration.checkIfMigrationNeeded(rootProject);
persistence.saveProject(rootProject, null);
currentProject = rootProject;
@@ -555,7 +559,6 @@ public class DefaultIndex extends SonarIndex {
}
resource.setEffectiveKey(ComponentKeys.createEffectiveKey(currentProject, resource));
- resource.setDeprecatedEffectiveKey(ComponentKeys.createDeprecatedEffectiveKey(currentProject, resource));
bucket = new Bucket(resource).setParent(parentBucket);
buckets.put(resource, bucket);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java
index 21b36dfc29e..4931060321c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java
@@ -89,6 +89,8 @@ public final class DefaultResourcePersister implements ResourcePersister {
project.setEffectiveKey(project.getKey());
ResourceModel model = findOrCreateModel(project);
+ // Used by ResourceKeyMigration in order to know that a project has already being migrated
+ model.setDeprecatedKey(project.getKey());
// ugly, only for projects
model.setLanguageKey(project.getLanguageKey());
@@ -271,10 +273,6 @@ public final class DefaultResourcePersister implements ResourcePersister {
ResourceModel model;
try {
model = session.getSingleResult(ResourceModel.class, "key", resource.getEffectiveKey());
- if (model == null && !StringUtils.equals(resource.getEffectiveKey(), resource.getDeprecatedEffectiveKey())) {
- // Fallback on deprecated key when resource has not already been migrated
- model = session.getSingleResult(ResourceModel.class, "key", resource.getDeprecatedEffectiveKey(), "deprecatedKey", null);
- }
if (model == null) {
if (StringUtils.isBlank(resource.getEffectiveKey())) {
throw new SonarException("Unable to persist resource " + resource.toString() + ". Resource effective key is blank. This may be caused by an outdated plugin.");
@@ -296,8 +294,6 @@ public final class DefaultResourcePersister implements ResourcePersister {
model.setEnabled(Boolean.TRUE);
model.setDescription(resource.getDescription());
model.setKey(resource.getEffectiveKey());
- String deprecatedEffectiveKey = resource.getDeprecatedEffectiveKey();
- model.setDeprecatedKey(StringUtils.isNotBlank(deprecatedEffectiveKey) ? deprecatedEffectiveKey : resource.getEffectiveKey());
model.setPath(resource.getPath());
if (resource.getLanguage() != null) {
model.setLanguageKey(resource.getLanguage().getKey());
@@ -316,12 +312,6 @@ public final class DefaultResourcePersister implements ResourcePersister {
static void mergeModel(ResourceModel model, Resource resource) {
model.setEnabled(true);
model.setKey(resource.getEffectiveKey());
- if (StringUtils.isNotBlank(resource.getDeprecatedEffectiveKey())) {
- model.setDeprecatedKey(resource.getDeprecatedEffectiveKey());
- } else if (StringUtils.isBlank(model.getDeprecatedKey())) {
- // By default deprecated key is the same as previous key
- model.setDeprecatedKey(model.getKey());
- }
if (StringUtils.isNotBlank(resource.getName())) {
model.setName(resource.getName());
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java b/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java
new file mode 100644
index 00000000000..ae97d13aa4e
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/ResourceKeyMigration.java
@@ -0,0 +1,174 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.index;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.scan.filesystem.internal.DefaultInputFile;
+import org.sonar.api.scan.filesystem.internal.InputFile;
+import org.sonar.api.utils.PathUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ResourceKeyMigration {
+
+ private final Logger logger;
+ private final DatabaseSession session;
+
+ private boolean migrationNeeded = false;
+
+ public ResourceKeyMigration(DatabaseSession session) {
+ this(session, LoggerFactory.getLogger(ResourceKeyMigration.class));
+ }
+
+ @VisibleForTesting
+ ResourceKeyMigration(DatabaseSession session, Logger logger) {
+ this.session = session;
+ this.logger = logger;
+ }
+
+ public void checkIfMigrationNeeded(Project rootProject) {
+ ResourceModel model = session.getSingleResult(ResourceModel.class, "key", rootProject.getEffectiveKey());
+ if (model != null && StringUtils.isBlank(model.getDeprecatedKey())) {
+ logger.info("Resources keys of project '" + rootProject.getName() + "' should be migrated");
+ this.migrationNeeded = true;
+ }
+ }
+
+ public void migrateIfNeeded(Project module, Iterable<InputFile> inputFiles) {
+ if (migrationNeeded) {
+ logger.info("Starting migration of resource keys");
+ Map<String, InputFile> deprecatedFileKeyMapper = new HashMap<String, InputFile>();
+ Map<String, InputFile> deprecatedTestKeyMapper = new HashMap<String, InputFile>();
+ Map<String, String> deprecatedDirectoryKeyMapper = new HashMap<String, String>();
+ for (InputFile inputFile : inputFiles) {
+ String deprecatedKey = inputFile.attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_DEPRECATED_KEY);
+ if (deprecatedKey != null) {
+ if (InputFile.TYPE_TEST.equals(inputFile.attribute(InputFile.ATTRIBUTE_TYPE))) {
+ deprecatedTestKeyMapper.put(deprecatedKey, inputFile);
+ } else {
+ deprecatedFileKeyMapper.put(deprecatedKey, inputFile);
+ }
+ }
+ }
+
+ ResourceModel moduleModel = session.getSingleResult(ResourceModel.class, "key", module.getEffectiveKey());
+ int moduleId = moduleModel.getId();
+ migrateFiles(module, deprecatedFileKeyMapper, deprecatedTestKeyMapper, deprecatedDirectoryKeyMapper, moduleId);
+ migrateDirectories(deprecatedDirectoryKeyMapper, moduleId);
+ session.commit();
+ }
+ }
+
+ private void migrateFiles(Project module, Map<String, InputFile> deprecatedFileKeyMapper, Map<String, InputFile> deprecatedTestKeyMapper,
+ Map<String, String> deprecatedDirectoryKeyMapper,
+ int moduleId) {
+ // Find all FIL or CLA resources for this module
+ StringBuilder hql = new StringBuilder().append("from ")
+ .append(ResourceModel.class.getSimpleName())
+ .append(" where enabled = true ")
+ .append(" and rootId = :rootId ")
+ .append(" and scope = '").append(Scopes.FILE).append("'");
+ List<ResourceModel> resources = session.createQuery(hql.toString()).setParameter("rootId", moduleId).getResultList();
+ for (ResourceModel resourceModel : resources) {
+ String oldEffectiveKey = resourceModel.getKey();
+ boolean isTest = Qualifiers.UNIT_TEST_FILE.equals(resourceModel.getQualifier());
+ InputFile matchedFile = findInputFile(deprecatedFileKeyMapper, deprecatedTestKeyMapper, oldEffectiveKey, isTest);
+ if (matchedFile != null) {
+ String newEffectiveKey = matchedFile.attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_KEY);
+ // Now compute migration of the parent dir
+ String oldKey = StringUtils.substringAfterLast(oldEffectiveKey, ":");
+ Resource sonarFile;
+ if (Java.KEY.equals(resourceModel.getLanguageKey())) {
+ sonarFile = new JavaFile(oldKey);
+ } else {
+ sonarFile = new File(oldKey);
+ }
+ String parentOldKey = module.getEffectiveKey() + ":" + sonarFile.getParent().getDeprecatedKey();
+ String parentNewKey = module.getEffectiveKey() + ":" + getParentKey(matchedFile);
+ if (!deprecatedDirectoryKeyMapper.containsKey(parentOldKey)) {
+ deprecatedDirectoryKeyMapper.put(parentOldKey, parentNewKey);
+ } else if (!parentNewKey.equals(deprecatedDirectoryKeyMapper.get(parentOldKey))) {
+ logger.warn("Directory with key " + parentOldKey + " matches both " + deprecatedDirectoryKeyMapper.get(parentOldKey) + " and "
+ + parentNewKey + ". First match is arbitrary chosen.");
+ }
+ resourceModel.setKey(newEffectiveKey);
+ resourceModel.setDeprecatedKey(oldEffectiveKey);
+ logger.info("Migrated resource " + oldEffectiveKey + " to " + newEffectiveKey);
+ } else {
+ logger.warn("Unable to migrate resource " + oldEffectiveKey + ". No match was found.");
+ }
+ }
+ }
+
+ private InputFile findInputFile(Map<String, InputFile> deprecatedFileKeyMapper, Map<String, InputFile> deprecatedTestKeyMapper, String oldEffectiveKey, boolean isTest) {
+ if (isTest) {
+ return deprecatedTestKeyMapper.get(oldEffectiveKey);
+ } else {
+ return deprecatedFileKeyMapper.get(oldEffectiveKey);
+ }
+ }
+
+ private void migrateDirectories(Map<String, String> deprecatedDirectoryKeyMapper, int moduleId) {
+ // Find all DIR resources for this module
+ StringBuilder hql = new StringBuilder().append("from ")
+ .append(ResourceModel.class.getSimpleName())
+ .append(" where enabled = true ")
+ .append(" and rootId = :rootId ")
+ .append(" and qualifier = '").append(Qualifiers.DIRECTORY).append("')");
+ List<ResourceModel> resources = session.createQuery(hql.toString()).setParameter("rootId", moduleId).getResultList();
+ for (ResourceModel resourceModel : resources) {
+ String oldEffectiveKey = resourceModel.getKey();
+ if (deprecatedDirectoryKeyMapper.containsKey(oldEffectiveKey)) {
+ String newEffectiveKey = deprecatedDirectoryKeyMapper.get(oldEffectiveKey);
+ resourceModel.setKey(newEffectiveKey);
+ resourceModel.setDeprecatedKey(oldEffectiveKey);
+ logger.info("Migrated resource " + oldEffectiveKey + " to " + newEffectiveKey);
+ } else {
+ logger.warn("Unable to migrate resource " + oldEffectiveKey);
+ }
+ }
+ }
+
+ private String getParentKey(InputFile matchedFile) {
+ String filePath = PathUtils.sanitize(matchedFile.path());
+ String parentFolderPath;
+ if (filePath.contains(Directory.SEPARATOR)) {
+ parentFolderPath = StringUtils.substringBeforeLast(filePath, Directory.SEPARATOR);
+ } else {
+ parentFolderPath = Directory.SEPARATOR;
+ }
+ return parentFolderPath;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/phases/FileIndexer.java
index 408557f9c2b..880f74d44e0 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/phases/FileIndexer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/phases/FileIndexer.java
@@ -35,40 +35,39 @@ import org.sonar.api.resources.Resource;
import org.sonar.api.scan.filesystem.FileQuery;
import org.sonar.api.scan.filesystem.internal.InputFile;
import org.sonar.api.utils.SonarException;
+import org.sonar.batch.index.ResourceKeyMigration;
import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
/**
- * Index all files/directories of the module in SQ database.
+ * Index all files/directories of the module in SQ database and importing source code.
* @since 4.2
*/
@InstantiationStrategy(InstantiationStrategy.PER_PROJECT)
public class FileIndexer implements BatchComponent {
- private final Project module;
private final DefaultModuleFileSystem fs;
private final Languages languages;
private final Settings settings;
private final SonarIndex sonarIndex;
+ private ResourceKeyMigration migration;
+ private Project module;
- private boolean importSource;
-
- public FileIndexer(Project module, DefaultModuleFileSystem fs, Languages languages, SonarIndex sonarIndex, Settings settings) {
+ public FileIndexer(Project module, DefaultModuleFileSystem fs, Languages languages, SonarIndex sonarIndex, Settings settings, ResourceKeyMigration migration) {
this.module = module;
this.fs = fs;
this.languages = languages;
this.sonarIndex = sonarIndex;
this.settings = settings;
+ this.migration = migration;
}
public void execute() {
- this.importSource = settings.getBoolean(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY);
- String languageKey = module.getLanguageKey();
- indexFiles(fs.inputFiles(FileQuery.onSource().onLanguage(languageKey)), false, languageKey);
- indexFiles(fs.inputFiles(FileQuery.onTest().onLanguage(languageKey)), true, languageKey);
- }
-
- private void indexFiles(Iterable<InputFile> files, boolean unitTest, String languageKey) {
- for (InputFile inputFile : files) {
+ boolean importSource = settings.getBoolean(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY);
+ Iterable<InputFile> inputFiles = fs.inputFiles(FileQuery.all());
+ migration.migrateIfNeeded(module, inputFiles);
+ for (InputFile inputFile : inputFiles) {
+ String languageKey = inputFile.attribute(InputFile.ATTRIBUTE_LANGUAGE);
+ boolean unitTest = InputFile.TYPE_TEST.equals(inputFile.attribute(InputFile.ATTRIBUTE_TYPE));
Resource sonarFile;
if (Java.KEY.equals(languageKey)) {
sonarFile = JavaFile.create(inputFile.path(), inputFile.attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH), unitTest);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
index 3edf7f278ec..cac9e05a534 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
@@ -52,6 +52,7 @@ import org.sonar.batch.index.LinkPersister;
import org.sonar.batch.index.MeasurePersister;
import org.sonar.batch.index.MemoryOptimizer;
import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.index.ResourceKeyMigration;
import org.sonar.batch.index.SnapshotCache;
import org.sonar.batch.index.SourcePersister;
import org.sonar.batch.issue.DefaultProjectIssues;
@@ -134,6 +135,7 @@ public class ProjectScanContainer extends ComponentContainer {
MetricProvider.class,
ProjectConfigurator.class,
DefaultIndex.class,
+ ResourceKeyMigration.class,
DefaultFileLinesContextFactory.class,
ProjectLock.class,
LastSnapshots.class,
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
index 87b15d8c4df..886ca6c0834 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
@@ -86,7 +86,7 @@ public class DefaultIndexTest {
ruleFinder = mock(RuleFinder.class);
ProjectTree projectTree = mock(ProjectTree.class);
- index = new DefaultIndex(mock(PersistenceManager.class), lock, projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations);
+ index = new DefaultIndex(mock(PersistenceManager.class), lock, projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.class));
java.io.File baseDir = temp.newFolder();
project = new Project("project");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java
index b08ab61d303..8799039f1ce 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java
@@ -146,7 +146,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase {
ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache);
persister.saveProject(singleProject, null);
persister.saveResource(singleProject,
- Directory.create("src/main/java/org/foo", "org.foo").setEffectiveKey("foo:src/main/java/org/foo").setDeprecatedEffectiveKey("foo:org.foo"));
+ Directory.create("src/main/java/org/foo", "org.foo").setEffectiveKey("foo:src/main/java/org/foo"));
// check that the directory is attached to the project
checkTables("shouldSaveNewDirectory", new String[] {"build_date", "created_at"}, "projects", "snapshots");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java
new file mode 100644
index 00000000000..aad9df2dce5
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/ResourceKeyMigrationTest.java
@@ -0,0 +1,128 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.index;
+
+import com.google.common.base.Charsets;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.sonar.api.resources.Project;
+import org.sonar.api.scan.filesystem.internal.DefaultInputFile;
+import org.sonar.api.scan.filesystem.internal.InputFile;
+import org.sonar.api.scan.filesystem.internal.InputFileBuilder;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class ResourceKeyMigrationTest extends AbstractDbUnitTestCase {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ Project multiModuleProject, phpModule, javaModule;
+ SnapshotCache snapshotCache = mock(SnapshotCache.class);
+ ResourceCache resourceCache = mock(ResourceCache.class);
+ private Iterable<InputFile> javaInputFiles;
+ private Iterable<InputFile> phpInputFiles;
+ private File baseDir;
+
+ @Before
+ public void before() throws ParseException, IOException {
+ SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
+
+ multiModuleProject = newProject("root", "java");
+ multiModuleProject.setName("Root").setAnalysisDate(format.parse("25/12/2010"));
+
+ phpModule = newProject("a", "php");
+ phpModule.setName("A").setAnalysisDate(format.parse("25/12/2010"));
+ phpModule.setParent(multiModuleProject);
+ phpModule.setPath("/moduleA");
+
+ javaModule = newProject("b", "java");
+ javaModule.setName("B").setAnalysisDate(format.parse("25/12/2010"));
+ javaModule.setParent(multiModuleProject);
+ javaModule.setPath("/moduleB");
+
+ baseDir = temp.newFolder();
+
+ javaInputFiles = (Iterable) Arrays.asList(
+ newInputFile(javaModule, "src/main/java/org/foo/Bar.java", "org.foo.Bar", false),
+ newInputFile(javaModule, "src/main/java/RootBar.java", "[default].RootBar", false),
+ newInputFile(javaModule, "src/test/java/org/foo/BarTest.java", "org.foo.BarTest", true));
+
+ phpInputFiles = (Iterable) Arrays.asList(
+ newInputFile(phpModule, "org/foo/Bar.php", "org/foo/Bar.php", false),
+ newInputFile(phpModule, "RootBar.php", "RootBar.php", false),
+ newInputFile(phpModule, "test/org/foo/BarTest.php", "org/foo/BarTest.php", true));
+
+ }
+
+ private DefaultInputFile newInputFile(Project module, String path, String deprecatedKey, boolean isTest) {
+ File file = new File(baseDir, path);
+ String effectiveKey = module.getKey() + ":" + path;
+ String deprecatedEffectiveKey = module.getKey() + ":" + deprecatedKey;
+ return new InputFileBuilder(file, Charsets.UTF_8, path)
+ .attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_KEY, effectiveKey)
+ .attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_DEPRECATED_KEY, deprecatedEffectiveKey)
+ .attribute(InputFile.ATTRIBUTE_TYPE, isTest ? InputFile.TYPE_TEST : InputFile.TYPE_SOURCE).build();
+ }
+
+ @Test
+ public void shouldMigrateResourceKeys() {
+ setupData("shouldMigrateResourceKeys");
+
+ Logger logger = mock(Logger.class);
+ ResourceKeyMigration migration = new ResourceKeyMigration(getSession(), logger);
+ migration.checkIfMigrationNeeded(multiModuleProject);
+ verify(logger).info("Resources keys of project 'Root' should be migrated");
+
+ migration.migrateIfNeeded(javaModule, javaInputFiles);
+ migration.migrateIfNeeded(phpModule, phpInputFiles);
+
+ verify(logger).info("Migrated resource b:org.foo.Bar to b:src/main/java/org/foo/Bar.java");
+ verify(logger).warn("Directory with key b:org/foo matches both b:src/main/java/org/foo and b:src/test/java/org/foo. First match is arbitrary chosen.");
+ verify(logger).info("Migrated resource b:org.foo.BarTest to b:src/test/java/org/foo/BarTest.java");
+ verify(logger).info("Migrated resource b:[default].RootBar to b:src/main/java/RootBar.java");
+ verify(logger).info("Migrated resource b:org/foo to b:src/main/java/org/foo");
+ verify(logger).info("Migrated resource b:[root] to b:src/main/java");
+
+ checkTables("shouldMigrateResourceKeys", new String[] {"build_date", "created_at"}, "projects");
+ }
+
+ private static Project newProject(String key, String language) {
+ PropertiesConfiguration configuration = new PropertiesConfiguration();
+ configuration.setProperty("sonar.language", language);
+ return new Project(key).setConfiguration(configuration).setAnalysisType(Project.AnalysisType.DYNAMIC);
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/FileIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/FileIndexerTest.java
index cf8d3aaf20c..e029d097752 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/phases/FileIndexerTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/phases/FileIndexerTest.java
@@ -20,7 +20,6 @@
package org.sonar.batch.phases;
import com.google.common.base.Charsets;
-import edu.emory.mathcs.backport.java.util.Collections;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.CharEncoding;
import org.junit.Before;
@@ -40,8 +39,10 @@ import org.sonar.api.resources.Project;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Resource;
import org.sonar.api.scan.filesystem.FileQuery;
+import org.sonar.api.scan.filesystem.internal.DefaultInputFile;
import org.sonar.api.scan.filesystem.internal.InputFile;
import org.sonar.api.scan.filesystem.internal.InputFileBuilder;
+import org.sonar.batch.index.ResourceKeyMigration;
import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
import java.io.File;
@@ -88,19 +89,13 @@ public class FileIndexerTest {
}
@Test
- public void should_index_java_files() {
- File javaFile1 = new File(baseDir, "src/main/java/foo/bar/Foo.java");
- File javaFile2 = new File(baseDir, "src/main/java2/foo/bar/Foo.java");
- when(fs.inputFiles(FileQuery.onSource().onLanguage(Java.KEY))).thenReturn((Iterable) Arrays.asList(
- new InputFileBuilder(javaFile1, Charsets.UTF_8, "src/main/java/foo/bar/Foo.java").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java").build(),
- new InputFileBuilder(javaFile2, Charsets.UTF_8, "src/main/java2/foo/bar/Foo.java").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java").build()));
- File javaTestFile1 = new File(baseDir, "src/test/java/foo/bar/FooTest.java");
- when(fs.inputFiles(FileQuery.onTest().onLanguage(Java.KEY))).thenReturn(
- (Iterable) Arrays.asList(
- new InputFileBuilder(javaTestFile1, Charsets.UTF_8, "src/test/java/foo/bar/FooTest.java").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/FooTest.java")
- .build()));
+ public void should_index_java_files() throws IOException {
+ when(fs.inputFiles(FileQuery.all())).thenReturn((Iterable) Arrays.asList(
+ newInputFile("src/main/java/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false),
+ newInputFile("src/main/java2/foo/bar/Foo.java", "", "foo/bar/Foo.java", "java", false),
+ newInputFile("src/test/java/foo/bar/FooTest.java", "", "foo/bar/FooTest.java", "java", true)));
when(project.getLanguageKey()).thenReturn(Java.KEY);
- FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings);
+ FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings, mock(ResourceKeyMigration.class));
indexer.execute();
verify(sonarIndex).index(JavaFile.create("src/main/java/foo/bar/Foo.java", "foo/bar/Foo.java", false));
@@ -118,17 +113,13 @@ public class FileIndexerTest {
@Test
public void should_index_cobol_files() throws IOException {
- File cobolFile1 = new File(baseDir, "src/foo/bar/Foo.cbl");
- File cobolFile2 = new File(baseDir, "src2/foo/bar/Foo.cbl");
- when(fs.inputFiles(FileQuery.onSource().onLanguage("cobol"))).thenReturn((Iterable) Arrays.asList(
- new InputFileBuilder(cobolFile1, Charsets.UTF_8, "src/foo/bar/Foo.cbl").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.cbl").build(),
- new InputFileBuilder(cobolFile2, Charsets.UTF_8, "src2/foo/bar/Foo.cbl").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.cbl").build()));
- File cobolTestFile1 = new File(baseDir, "src/test/foo/bar/FooTest.cbl");
- when(fs.inputFiles(FileQuery.onTest().onLanguage("cobol"))).thenReturn((Iterable) Arrays.asList(
- new InputFileBuilder(cobolTestFile1, Charsets.UTF_8, "src/test/foo/bar/FooTest.cbl").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/FooTest.cbl").build()));
+ when(fs.inputFiles(FileQuery.all())).thenReturn((Iterable) Arrays.asList(
+ newInputFile("src/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false),
+ newInputFile("src2/foo/bar/Foo.cbl", "", "foo/bar/Foo.cbl", "cobol", false),
+ newInputFile("src/test/foo/bar/FooTest.cbl", "", "foo/bar/FooTest.cbl", "cobol", true)));
when(project.getLanguageKey()).thenReturn("cobol");
- FileIndexer indexer = new FileIndexer(project, fs, new Languages(cobolLanguage), sonarIndex, settings);
+ FileIndexer indexer = new FileIndexer(project, fs, new Languages(cobolLanguage), sonarIndex, settings, mock(ResourceKeyMigration.class));
indexer.execute();
verify(sonarIndex).index(org.sonar.api.resources.File.create("/src/foo/bar/Foo.cbl", "foo/bar/Foo.cbl", cobolLanguage, false));
@@ -140,14 +131,10 @@ public class FileIndexerTest {
public void shouldImportSource() throws IOException {
settings.setProperty(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY, "true");
- File javaFile1 = new File(baseDir, "src/main/java/foo/bar/Foo.java");
- FileUtils.write(javaFile1, "sample code");
- when(fs.inputFiles(FileQuery.onSource().onLanguage(Java.KEY))).thenReturn((Iterable) Arrays.asList(
- new InputFileBuilder(javaFile1, Charsets.UTF_8, "src/main/java/foo/bar/Foo.java").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java").build()));
- when(fs.inputFiles(FileQuery.onTest().onLanguage(Java.KEY))).thenReturn(
- (Iterable) Collections.emptyList());
+ when(fs.inputFiles(FileQuery.all())).thenReturn((Iterable) Arrays.asList(
+ newInputFile("src/main/java/foo/bar/Foo.java", "sample code", "foo/bar/Foo.java", "java", false)));
when(project.getLanguageKey()).thenReturn(Java.KEY);
- FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings);
+ FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings, mock(ResourceKeyMigration.class));
indexer.execute();
Resource sonarFile = JavaFile.create("src/main/java/foo/bar/Foo.java", "foo/bar/Foo.java", false);
@@ -155,6 +142,16 @@ public class FileIndexerTest {
verify(sonarIndex).setSource(sonarFile, "sample code");
}
+ private DefaultInputFile newInputFile(String path, String content, String sourceRelativePath, String languageKey, boolean unitTest) throws IOException {
+ File file = new File(baseDir, path);
+ FileUtils.write(file, content);
+ return new InputFileBuilder(file, Charsets.UTF_8, path)
+ .attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, sourceRelativePath)
+ .attribute(InputFile.ATTRIBUTE_LANGUAGE, languageKey)
+ .attribute(InputFile.ATTRIBUTE_TYPE, unitTest ? InputFile.TYPE_TEST : InputFile.TYPE_SOURCE)
+ .build();
+ }
+
@Test
public void should_use_mac_roman_charset_for_reading_source_files() throws Exception {
String encoding = "MacRoman";
@@ -182,12 +179,13 @@ public class FileIndexerTest {
File javaFile1 = new File(baseDir, "src/main/java/foo/bar/Foo.java");
FileUtils.write(javaFile1, "\uFEFFpublic class Test", Charsets.UTF_8);
- when(fs.inputFiles(FileQuery.onSource().onLanguage(Java.KEY))).thenReturn((Iterable) Arrays.asList(
- new InputFileBuilder(javaFile1, Charsets.UTF_8, "src/main/java/foo/bar/Foo.java").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java").build()));
- when(fs.inputFiles(FileQuery.onTest().onLanguage(Java.KEY))).thenReturn(
- (Iterable) Collections.emptyList());
+ when(fs.inputFiles(FileQuery.all())).thenReturn((Iterable) Arrays.asList(
+ new InputFileBuilder(javaFile1, Charset.forName("UTF-8"), "src/main/java/foo/bar/Foo.java")
+ .attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java")
+ .attribute(InputFile.ATTRIBUTE_LANGUAGE, "java")
+ .build()));
when(project.getLanguageKey()).thenReturn(Java.KEY);
- FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings);
+ FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings, mock(ResourceKeyMigration.class));
indexer.execute();
Resource sonarFile = JavaFile.create("src/main/java/foo/bar/Foo.java", "foo/bar/Foo.java", false);
@@ -206,15 +204,15 @@ public class FileIndexerTest {
File javaFile1 = new File(baseDir, "src/main/java/foo/bar/Foo.java");
FileUtils.copyFile(getFile(testFile), javaFile1);
- when(fs.inputFiles(FileQuery.onSource().onLanguage(Java.KEY)))
+ when(fs.inputFiles(FileQuery.all()))
.thenReturn(
(Iterable) Arrays.asList(
- new InputFileBuilder(javaFile1, Charset.forName(encoding), "src/main/java/foo/bar/Foo.java").attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java")
+ new InputFileBuilder(javaFile1, Charset.forName(encoding), "src/main/java/foo/bar/Foo.java")
+ .attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH, "foo/bar/Foo.java")
+ .attribute(InputFile.ATTRIBUTE_LANGUAGE, "java")
.build()));
- when(fs.inputFiles(FileQuery.onTest().onLanguage(Java.KEY))).thenReturn(
- (Iterable) Collections.emptyList());
when(project.getLanguageKey()).thenReturn(Java.KEY);
- FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings);
+ FileIndexer indexer = new FileIndexer(project, fs, new Languages(Java.INSTANCE), sonarIndex, settings, mock(ResourceKeyMigration.class));
indexer.execute();
Resource sonarFile = JavaFile.create("/src/main/java/foo/bar/Foo.java", "foo/bar/Foo.java", false);
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewDirectory-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewDirectory-result.xml
index fee6de33350..6ca27edcd8b 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewDirectory-result.xml
+++ b/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewDirectory-result.xml
@@ -17,7 +17,7 @@
<projects id="1002" scope="DIR" qualifier="DIR" kee="foo:src/main/java/org/foo" root_id="1001"
name="src/main/java/org/foo" long_name="src/main/java/org/foo" description="[null]"
- enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/main/java/org/foo" deprecated_kee="foo:org.foo" />
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/main/java/org/foo" deprecated_kee="[null]" />
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="3001" project_id="1001" parent_snapshot_id="[null]" root_project_id="1001" root_snapshot_id="[null]"
scope="PRJ" qualifier="TRK" created_at="2010-12-25 00:00:00.00" build_date="2010-12-25 00:00:00.00" version="[null]" path=""
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewLibrary-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewLibrary-result.xml
index 745203dabf1..a7107a2871a 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewLibrary-result.xml
+++ b/sonar-batch/src/test/resources/org/sonar/batch/index/DefaultResourcePersisterTest/shouldSaveNewLibrary-result.xml
@@ -17,7 +17,7 @@
<projects id="1002" scope="PRJ" qualifier="LIB" kee="junit:junit" root_id="[null]"
name="junit:junit" long_name="junit:junit" description="[null]"
- enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" deprecated_kee="junit:junit" />
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" deprecated_kee="[null]" />
<snapshots purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]" period2_param="[null]" period2_date="[null]" period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]" period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]" period5_date="[null]" id="3001" project_id="1001" parent_snapshot_id="[null]" root_project_id="1001" root_snapshot_id="[null]"
scope="PRJ" qualifier="TRK" created_at="2010-12-25 00:00:00.00" build_date="2010-12-25 00:00:00.00" version="[null]" path=""
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys-result.xml
new file mode 100644
index 00000000000..3dacd66ea57
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys-result.xml
@@ -0,0 +1,65 @@
+<dataset>
+
+ <!-- other project already migrated -->
+ <projects id="1000" scope="PRJ" qualifier="TRK" kee="my:key" root_id="[null]"
+ name="Other project" long_name="Other" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="my:key" />
+
+ <!-- Root project not migrated -->
+ <projects id="1001" scope="PRJ" qualifier="TRK" kee="root" root_id="[null]"
+ name="Root" long_name="Root" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <!-- PHP module -->
+ <projects id="1002" scope="PRJ" qualifier="BRC" kee="a" root_id="1001"
+ name="A" long_name="A" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <!-- Java module -->
+ <projects id="1003" scope="PRJ" qualifier="BRC" kee="b" root_id="1001"
+ name="B" long_name="B" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <!-- Java resources -->
+ <projects id="1004" scope="DIR" qualifier="DIR" kee="b:src/main/java/org/foo" root_id="1003"
+ name="org/foo" long_name="org/foo" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="b:org/foo" />
+
+ <projects id="1005" scope="FIL" qualifier="CLA" kee="b:src/main/java/org/foo/Bar.java" root_id="1003"
+ name="Bar" long_name="org.foo.Bar" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="b:org.foo.Bar" />
+
+ <projects id="1006" scope="FIL" qualifier="UTS" kee="b:src/test/java/org/foo/BarTest.java" root_id="1003"
+ name="BarTest" long_name="org.foo.BarTest" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="b:org.foo.BarTest" />
+
+ <projects id="1007" scope="DIR" qualifier="DIR" kee="b:src/main/java" root_id="1003"
+ name="[root]" long_name="[root]" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="b:[root]" />
+
+ <projects id="1008" scope="FIL" qualifier="CLA" kee="b:src/main/java/RootBar.java" root_id="1003"
+ name="RootBar" long_name="RootBar" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="b:[default].RootBar" />
+
+
+ <!-- PHP resources -->
+ <projects id="1009" scope="DIR" qualifier="DIR" kee="a:org/foo" root_id="1002"
+ name="org/foo" long_name="org/foo" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="a:org/foo" />
+
+ <projects id="1010" scope="FIL" qualifier="FIL" kee="a:org/foo/Bar.php" root_id="1002"
+ name="Bar.php" long_name="Bar.php" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="a:org/foo/Bar.php" />
+
+ <projects id="1011" scope="FIL" qualifier="UTS" kee="a:test/org/foo/BarTest.php" root_id="1002"
+ name="BarTest.php" long_name="BarTest.php" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="a:org/foo/BarTest.php" />
+
+ <projects id="1012" scope="DIR" qualifier="DIR" kee="a:/" root_id="1002"
+ name="[root]" long_name="[root]" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="a:[root]" />
+
+ <projects id="1013" scope="FIL" qualifier="FIL" kee="a:RootBar.php" root_id="1002"
+ name="RootBar.php" long_name="RootBar.php" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="a:RootBar.php" />
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys.xml
new file mode 100644
index 00000000000..4cfae50cb46
--- /dev/null
+++ b/sonar-batch/src/test/resources/org/sonar/batch/index/ResourceKeyMigrationTest/shouldMigrateResourceKeys.xml
@@ -0,0 +1,65 @@
+<dataset>
+
+ <!-- other project already migrated -->
+ <projects id="1000" scope="PRJ" qualifier="TRK" kee="my:key" root_id="[null]"
+ name="Other project" long_name="Other" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="my:key" />
+
+ <!-- Root project not migrated -->
+ <projects id="1001" scope="PRJ" qualifier="TRK" kee="root" root_id="[null]"
+ name="Root" long_name="Root" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <!-- PHP module -->
+ <projects id="1002" scope="PRJ" qualifier="BRC" kee="a" root_id="1001"
+ name="A" long_name="A" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <!-- Java module -->
+ <projects id="1003" scope="PRJ" qualifier="BRC" kee="b" root_id="1001"
+ name="B" long_name="B" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <!-- Java resources -->
+ <projects id="1004" scope="DIR" qualifier="DIR" kee="b:org/foo" root_id="1003"
+ name="org/foo" long_name="org/foo" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1005" scope="FIL" qualifier="CLA" kee="b:org.foo.Bar" root_id="1003"
+ name="Bar" long_name="org.foo.Bar" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1006" scope="FIL" qualifier="UTS" kee="b:org.foo.BarTest" root_id="1003"
+ name="BarTest" long_name="org.foo.BarTest" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1007" scope="DIR" qualifier="DIR" kee="b:[root]" root_id="1003"
+ name="[root]" long_name="[root]" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1008" scope="FIL" qualifier="CLA" kee="b:[default].RootBar" root_id="1003"
+ name="RootBar" long_name="RootBar" description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+
+ <!-- PHP resources -->
+ <projects id="1009" scope="DIR" qualifier="DIR" kee="a:org/foo" root_id="1002"
+ name="org/foo" long_name="org/foo" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1010" scope="FIL" qualifier="FIL" kee="a:org/foo/Bar.php" root_id="1002"
+ name="Bar.php" long_name="Bar.php" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1011" scope="FIL" qualifier="UTS" kee="a:org/foo/BarTest.php" root_id="1002"
+ name="BarTest.php" long_name="BarTest.php" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1012" scope="DIR" qualifier="DIR" kee="a:[root]" root_id="1002"
+ name="[root]" long_name="[root]" description="[null]"
+ enabled="true" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+
+ <projects id="1013" scope="FIL" qualifier="FIL" kee="a:RootBar.php" root_id="1002"
+ name="RootBar.php" long_name="RootBar.php" description="[null]"
+ enabled="true" language="php" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]" />
+</dataset>