From 894305bb873f56d55d4acff8a5657a1cd966ab0c Mon Sep 17 00:00:00 2001 From: Sébastien Lesaint Date: Fri, 25 Mar 2016 16:08:24 +0100 Subject: SONAR-7480 optimize SQL request in QP Search WS --- .../server/qualityprofile/QProfileFactory.java | 15 ++ .../server/qualityprofile/QProfileLoader.java | 6 +- .../server/qualityprofile/ws/SearchAction.java | 6 +- .../server/qualityprofile/ws/SearchDataLoader.java | 205 +++++++++++++-------- .../sonar/db/qualityprofile/QualityProfileDao.java | 41 ++++- .../db/qualityprofile/QualityProfileMapper.java | 6 + .../db/qualityprofile/QualityProfileMapper.xml | 49 ++++- .../db/qualityprofile/QualityProfileDaoTest.java | 42 +++++ 8 files changed, 282 insertions(+), 88 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java index f5e458a2777..29903ee8dcc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java @@ -21,8 +21,10 @@ package org.sonar.server.qualityprofile; import com.google.common.collect.Lists; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Set; import javax.annotation.CheckForNull; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; @@ -138,6 +140,11 @@ public class QProfileFactory { return db.qualityProfileDao().selectDefaultProfile(session, language); } + @CheckForNull + public List getDefaults(DbSession session, Collection languageKeys) { + return db.qualityProfileDao().selectDefaultProfiles(session, languageKeys); + } + public void setDefault(String profileKey) { DbSession dbSession = db.openSession(false); try { @@ -179,6 +186,10 @@ public class QProfileFactory { return db.qualityProfileDao().selectByProjectAndLanguage(session, projectKey, language); } + public List getByProjectAndLanguages(DbSession session, String projectKey, Set languageKeys) { + return db.qualityProfileDao().selectByProjectAndLanguages(session, projectKey, languageKeys); + } + QualityProfileDto getByNameAndLanguage(String name, String language) { DbSession dbSession = db.openSession(false); try { @@ -193,6 +204,10 @@ public class QProfileFactory { return db.qualityProfileDao().selectByNameAndLanguage(name, language, session); } + public List getByNameAndLanguages(DbSession session, String name, Collection languages) { + return db.qualityProfileDao().selectByNameAndLanguages(name, languages, session); + } + private void checkNotDefault(QualityProfileDto p) { if (p.isDefault()) { throw new BadRequestException("The profile marked as default can not be deleted: " + p.getKey()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java index 1e23d75a0f9..64230d8b40b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import javax.annotation.CheckForNull; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; @@ -100,8 +101,9 @@ public class QProfileLoader { } public Map countAllActiveRules() { - Map counts = new HashMap<>(); - for (Map.Entry entry : activeRuleIndex.countAllByQualityProfileKey().entrySet()) { + Set> entries = activeRuleIndex.countAllByQualityProfileKey().entrySet(); + Map counts = new HashMap<>(entries.size()); + for (Map.Entry entry : entries) { counts.put(entry.getKey(), entry.getValue()); } return counts; diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java index 033280ff0e6..d05d1fa7a1b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java @@ -108,7 +108,7 @@ public class SearchAction implements QProfileWsAction { private SearchWsResponse buildResponse(SearchData data) { List profiles = data.getProfiles(); - Map profilesByKey = uniqueIndex(profiles, new QProfileToKey()); + Map profilesByKey = uniqueIndex(profiles, QProfileToKey.INSTANCE); QualityProfiles.SearchWsResponse.Builder response = QualityProfiles.SearchWsResponse.newBuilder(); QualityProfile.Builder profileBuilder = QualityProfile.newBuilder(); @@ -169,7 +169,9 @@ public class SearchAction implements QProfileWsAction { return value != null; } - private static class QProfileToKey implements Function { + private enum QProfileToKey implements Function { + INSTANCE; + @Override public String apply(@Nonnull QProfile input) { return input.key(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java index 0ee94761834..71348447368 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java @@ -19,10 +19,16 @@ */ package org.sonar.server.qualityprofile.ws; +import com.google.common.base.Function; import com.google.common.base.Predicate; -import java.util.ArrayList; +import com.google.common.collect.Sets; +import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang.builder.CompareToBuilder; @@ -39,7 +45,6 @@ import org.sonar.server.qualityprofile.QProfileLoader; import org.sonar.server.qualityprofile.QProfileLookup; import org.sonarqube.ws.client.qualityprofile.SearchWsRequest; -import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.FluentIterable.from; import static java.lang.String.format; import static org.sonar.server.ws.WsUtils.checkRequest; @@ -51,6 +56,7 @@ public class SearchDataLoader { private final QProfileFactory profileFactory; private final DbClient dbClient; private final ComponentFinder componentFinder; + private final IsLanguageKnown isLanguageKnown; public SearchDataLoader(Languages languages, QProfileLookup profileLookup, QProfileLoader profileLoader, QProfileFactory profileFactory, DbClient dbClient, ComponentFinder componentFinder) { @@ -60,6 +66,7 @@ public class SearchDataLoader { this.profileFactory = profileFactory; this.dbClient = dbClient; this.componentFinder = componentFinder; + this.isLanguageKnown = new IsLanguageKnown(); } SearchData load(SearchWsRequest request) { @@ -72,7 +79,7 @@ public class SearchDataLoader { } private List findProfiles(SearchWsRequest request) { - List profiles; + Collection profiles; if (askDefaultProfiles(request)) { profiles = findDefaultProfiles(request); } else if (hasComponentKey(request)) { @@ -81,105 +88,125 @@ public class SearchDataLoader { profiles = findAllProfiles(request); } - return orderProfiles(profiles); + return from(profiles).toSortedList(QProfileComparator.INSTANCE); } - private static List orderProfiles(List profiles) { - return from(profiles) - .toSortedList(QProfileComparator.INSTANCE); - } + private Collection findDefaultProfiles(SearchWsRequest request) { + String profileName = request.getProfileName(); - private List findAllProfiles(SearchWsRequest request) { - String language = request.getLanguage(); + Set languageKeys = getLanguageKeys(); + try (DbSession dbSession = dbClient.openSession(false)) { + Map qualityProfiles = new HashMap<>(languageKeys.size()); - List profiles = language != null ? profileLookup.profiles(language) - : profileLookup.allProfiles(); + Set missingLanguageKeys = lookupByProfileName(dbSession, qualityProfiles, languageKeys, profileName); + Set noDefaultProfileLanguageKeys = lookupDefaults(dbSession, qualityProfiles, missingLanguageKeys); - return from(profiles) - .filter(new IsLanguageKnown()) - .toList(); + if (!noDefaultProfileLanguageKeys.isEmpty()) { + throw new IllegalStateException(format("No quality profile can been found on language(s) '%s'", noDefaultProfileLanguageKeys)); + } + + return qualityProfiles.values(); + } } - private List findProjectProfiles(SearchWsRequest request) { - String moduleKey = request.getProjectKey(); + private Collection findProjectProfiles(SearchWsRequest request) { + String componentKey = request.getProjectKey(); String profileName = request.getProfileName(); - List profiles = new ArrayList<>(); + Set languageKeys = getLanguageKeys(); + try (DbSession dbSession = dbClient.openSession(false)) { + Map qualityProfiles = new HashMap<>(languageKeys.size()); + + // look up profiles by profileName (if any) for each language + Set unresolvedLanguages = lookupByProfileName(dbSession, qualityProfiles, languageKeys, profileName); + // look up profile by componentKey for each language for which we don't have one yet + Set stillUnresolvedLanguages = lookupByModuleKey(dbSession, qualityProfiles, unresolvedLanguages, componentKey); + // look up profile by default for each language for which we don't have one yet + Set noDefaultProfileLanguages = lookupDefaults(dbSession, qualityProfiles, stillUnresolvedLanguages); - DbSession dbSession = dbClient.openSession(false); - try { - for (Language language : languages.all()) { - String languageKey = language.getKey(); - ComponentDto project = getProject(moduleKey, dbSession); - profiles.add(getProfile(dbSession, languageKey, project.key(), profileName)); + if (!noDefaultProfileLanguages.isEmpty()) { + throw new IllegalStateException(format("No quality profile can been found on language(s) '%s' for project '%s'", noDefaultProfileLanguages, componentKey)); } - } finally { - dbClient.closeSession(dbSession); + + return qualityProfiles.values(); } + } + + private List findAllProfiles(SearchWsRequest request) { + String language = request.getLanguage(); - return profiles; + if (language == null) { + return from(profileLookup.allProfiles()).filter(isLanguageKnown).toList(); + } + return profileLookup.profiles(language); + } + + private Set lookupByProfileName(DbSession dbSession, Map qualityProfiles, Set languageKeys, @Nullable String profileName) { + if (languageKeys.isEmpty() || profileName == null) { + return languageKeys; + } + + addAllFromDto(qualityProfiles, profileFactory.getByNameAndLanguages(dbSession, profileName, languageKeys)); + return difference(languageKeys, qualityProfiles.keySet()); + } + + private Set lookupByModuleKey(DbSession dbSession, Map qualityProfiles, Set languageKeys, @Nullable String moduleKey) { + if (languageKeys.isEmpty() || moduleKey == null) { + return languageKeys; + } + + ComponentDto project = getProject(moduleKey, dbSession); + addAllFromDto(qualityProfiles, profileFactory.getByProjectAndLanguages(dbSession, project.getKey(), languageKeys)); + return difference(languageKeys, qualityProfiles.keySet()); } private ComponentDto getProject(String moduleKey, DbSession session) { ComponentDto module = componentFinder.getByKey(session, moduleKey); - if (!module.isRootProject()) { - return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid()); - } else { + if (module.isRootProject()) { return module; } + return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid()); } - private List findDefaultProfiles(SearchWsRequest request) { - String profileName = request.getProfileName(); - List profiles = new ArrayList<>(); + private Set lookupDefaults(DbSession dbSession, Map qualityProfiles, Set languageKeys) { + if (languageKeys.isEmpty()) { + return languageKeys; + } - DbSession dbSession = dbClient.openSession(false); - try { - for (Language language : languages.all()) { - profiles.add(getDefaultProfile(dbSession, language.getKey(), profileName)); - } - } finally { - dbClient.closeSession(dbSession); + addAll(qualityProfiles, findDefaultProfiles(dbSession, languageKeys)); + return difference(languageKeys, qualityProfiles.keySet()); + } + + private static Set difference(Set languageKeys, Set set2) { + return Sets.newHashSet(Sets.difference(languageKeys, set2)); + } + + private void addAllFromDto(Map qualityProfiles, Collection list) { + for (QualityProfileDto qualityProfileDto : list) { + qualityProfiles.put(qualityProfileDto.getLanguage(), QualityProfileDtoToQProfile.INSTANCE.apply(qualityProfileDto)); } + } - return profiles; - } - - private static QProfile profileDtoToQProfile(QualityProfileDto dto) { - return new QProfile() - .setKey(dto.getKey()) - .setName(dto.getName()) - .setLanguage(dto.getLanguage()) - .setDefault(dto.isDefault()) - .setRulesUpdatedAt(dto.getRulesUpdatedAt()); - } - - /** - * First try to find a quality profile matching the given name (if provided) and current language - * If no profile found, try to find the quality profile set on the project (if provided) - * If still no profile found, try to find the default profile of the language - *

- * Never return null because a default profile should always be set on each language - */ - private QProfile getProfile(DbSession dbSession, String languageKey, @Nullable String projectKey, @Nullable String profileName) { - QualityProfileDto profileDto = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null; - if (profileDto == null && projectKey != null) { - profileDto = profileFactory.getByProjectAndLanguage(dbSession, projectKey, languageKey); + private void addAll(Map qualityProfiles, Collection list) { + for (QProfile qProfile : list) { + qualityProfiles.put(qProfile.language(), qProfile); } - profileDto = profileDto != null ? profileDto : profileFactory.getDefault(dbSession, languageKey); - checkState(profileDto != null, format("No quality profile can been found on language '%s' for project '%s'", languageKey, projectKey)); + } - return profileDtoToQProfile(profileDto); + private Set getLanguageKeys() { + return from(Arrays.asList(languages.all())).transform(LanguageToKey.INSTANCE).toSet(); } - private QProfile getDefaultProfile(DbSession dbSession, String languageKey, @Nullable String profileName) { - QualityProfileDto profile = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null; - if (profile == null) { - profile = profileFactory.getDefault(dbSession, languageKey); + private List findDefaultProfiles(Set languageKeys) { + try (DbSession dbSession = dbClient.openSession(false)) { + return findDefaultProfiles(dbSession, languageKeys); } - checkState(profile != null, format("No quality profile can been found on language '%s'", languageKey)); + } - return profileDtoToQProfile(profile); + private List findDefaultProfiles(final DbSession dbSession, Set languageKeys) { + return from(profileFactory.getDefaults(dbSession, languageKeys)) + .transform(QualityProfileDtoToQProfile.INSTANCE) + .toList(); } private static void validateRequest(SearchWsRequest request) { @@ -193,6 +220,10 @@ public class SearchDataLoader { checkRequest(!isDefault || !hasComponentKey, "The default parameter cannot be provided at the same time than the component key"); } + private static boolean askDefaultProfiles(SearchWsRequest request) { + return request.getDefaults(); + } + private static boolean hasProfileName(SearchWsRequest request) { return request.getProfileName() != null; } @@ -201,10 +232,6 @@ public class SearchDataLoader { return request.getProjectKey() != null; } - private static Boolean askDefaultProfiles(SearchWsRequest request) { - return request.getDefaults(); - } - private static boolean hasLanguage(SearchWsRequest request) { return request.getLanguage() != null; } @@ -220,6 +247,32 @@ public class SearchDataLoader { } } + private enum LanguageToKey implements Function { + INSTANCE; + + @Override + @Nonnull + public String apply(@Nonnull Language input) { + return input.getKey(); + } + } + + private enum QualityProfileDtoToQProfile implements Function { + INSTANCE; + + @Override + @Nonnull + public QProfile apply(@Nonnull QualityProfileDto input) { + return new QProfile() + .setKey(input.getKey()) + .setName(input.getName()) + .setLanguage(input.getLanguage()) + .setDefault(input.isDefault()) + .setRulesUpdatedAt(input.getRulesUpdatedAt()); + } + + } + private class IsLanguageKnown implements Predicate { @Override public boolean apply(@Nonnull QProfile profile) { diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java index ca2ec555f1c..fbe2d1913c2 100644 --- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java @@ -19,16 +19,20 @@ */ package org.sonar.db.qualityprofile; +import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.sonar.api.utils.System2; import org.sonar.db.Dao; +import org.sonar.db.DatabaseUtils; import org.sonar.db.DbSession; import org.sonar.db.MyBatis; import org.sonar.db.RowNotFoundException; @@ -155,8 +159,7 @@ public class QualityProfileDao implements Dao { } /** - * @deprecated Replaced by - * {@link #selectAll(DbSession)} + * @deprecated Replaced by {@link #selectAll(DbSession)} */ @Deprecated public List selectAll() { @@ -169,7 +172,18 @@ public class QualityProfileDao implements Dao { } @CheckForNull - public QualityProfileDto selectDefaultProfile(DbSession session, String language) { + public List selectDefaultProfiles(final DbSession session, Collection languageKeys) { + return DatabaseUtils.executeLargeInputs(languageKeys, new Function, List>() { + @Override + @Nonnull + public List apply(@Nonnull List input) { + return mapper(session).selectDefaultProfiles(input); + } + }); + } + + @CheckForNull + public QualityProfileDto selectDefaultProfile(final DbSession session, String language) { return mapper(session).selectDefaultProfile(language); } @@ -198,6 +212,16 @@ public class QualityProfileDao implements Dao { return mapper(session).selectByProjectAndLanguage(projectKey, language); } + public List selectByProjectAndLanguages(final DbSession session, final String projectKey, Collection languageKeys) { + return DatabaseUtils.executeLargeInputs(languageKeys, new Function, List>() { + @Override + @Nonnull + public List apply(@Nonnull List input) { + return mapper(session).selectByProjectAndLanguages(projectKey, input); + } + }); + } + public List selectByLanguage(String language) { DbSession session = mybatis.openSession(false); try { @@ -273,6 +297,16 @@ public class QualityProfileDao implements Dao { return mapper(session).selectByNameAndLanguage(name, language); } + public List selectByNameAndLanguages(final String name, Collection languageKeys, final DbSession session) { + return DatabaseUtils.executeLargeInputs(languageKeys, new Function, List>() { + @Override + @Nonnull + public List apply(@Nonnull List input) { + return mapper(session).selectByNameAndLanguages(name, input); + } + }); + } + public List selectProjects(String profileName, String language) { DbSession session = mybatis.openSession(false); try { @@ -346,5 +380,4 @@ public class QualityProfileDao implements Dao { private QualityProfileMapper mapper(DbSession session) { return session.getMapper(QualityProfileMapper.class); } - } diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java index d2f9e73a959..b8a6ef79c09 100644 --- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java @@ -37,9 +37,13 @@ public interface QualityProfileMapper { @CheckForNull QualityProfileDto selectDefaultProfile(@Param("language") String language); + List selectDefaultProfiles(@Param("languages") List languages); + @CheckForNull QualityProfileDto selectByNameAndLanguage(@Param("name") String name, @Param("language") String language); + List selectByNameAndLanguages(@Param("name") String name, @Param("languages") List languages); + @CheckForNull QualityProfileDto selectById(@Param("id") Integer id); @@ -70,6 +74,8 @@ public interface QualityProfileMapper { QualityProfileDto selectByProjectAndLanguage(@Param("projectKey") String projectKey, @Param("language") String language); + List selectByProjectAndLanguages(@Param("projectKey") String projectKey, @Param("languages") List input); + void insertProjectProfileAssociation(@Param("projectUuid") String projectUuid, @Param("profileKey") String profileKey); void updateProjectProfileAssociation(@Param("projectUuid") String projectUuid, @Param("profileKey") String profileKey); diff --git a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml index 873a983c4cb..edc2563f8e2 100644 --- a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml @@ -49,6 +49,19 @@ WHERE p.name=#{name} AND p.language=#{language} + + + + + + INSERT INTO project_qprofiles (project_uuid, profile_key) VALUES (#{projectUuid}, #{profileKey}) diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java index f53a1edd2b1..d47a6e19467 100644 --- a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java @@ -28,6 +28,8 @@ import org.sonar.api.utils.System2; import org.sonar.core.util.UtcDateUtils; import org.sonar.db.DbTester; +import static com.google.common.collect.ImmutableList.of; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -129,6 +131,18 @@ public class QualityProfileDaoTest { assertThat(dao.selectDefaultProfile("js")).isNull(); } + @Test + public void get_default_profiles() { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + + List java = dao.selectDefaultProfiles(dbTester.getSession(), singletonList("java")); + assertThat(java).extracting("key").containsOnly("java_sonar_way"); + + assertThat(dao.selectDefaultProfiles(dbTester.getSession(), singletonList("js"))).isEmpty(); + assertThat(dao.selectDefaultProfiles(dbTester.getSession(), of("java", "js"))).extracting("key").containsOnly("java_sonar_way"); + assertThat(dao.selectDefaultProfiles(dbTester.getSession(), of("js", "java"))).extracting("key").containsOnly("java_sonar_way"); + } + @Test public void get_by_name_and_language() { dbTester.prepareDbUnit(getClass(), "shared.xml"); @@ -143,6 +157,22 @@ public class QualityProfileDaoTest { assertThat(dao.selectByNameAndLanguage("Sonar Way", "unknown", dbTester.getSession())).isNull(); } + @Test + public void get_by_name_and_languages() { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + + List dtos = dao.selectByNameAndLanguages("Sonar Way", singletonList("java"), dbTester.getSession()); + assertThat(dtos).hasSize(1); + QualityProfileDto dto = dtos.iterator().next(); + assertThat(dto.getId()).isEqualTo(1); + assertThat(dto.getName()).isEqualTo("Sonar Way"); + assertThat(dto.getLanguage()).isEqualTo("java"); + assertThat(dto.getParentKee()).isNull(); + + assertThat(dao.selectByNameAndLanguages("Sonar Way", singletonList("unknown"), dbTester.getSession())).isEmpty(); + assertThat(dao.selectByNameAndLanguages("Sonar Way", of("java", "unknown"), dbTester.getSession())).extracting("id").containsOnly(1); + } + @Test public void find_by_language() { dbTester.prepareDbUnit(getClass(), "select_by_language.xml"); @@ -236,4 +266,16 @@ public class QualityProfileDaoTest { assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "unkown")).isNull(); assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "unknown", "java")).isNull(); } + + @Test + public void select_by_project_key_and_languages() { + dbTester.prepareDbUnit(getClass(), "projects.xml"); + + List dto = dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("java")); + assertThat(dto).extracting("id").containsOnly(1); + + assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("unkown"))).isEmpty(); + assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", of("java", "unkown"))).extracting("id").containsOnly(1); + assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "unknown", singletonList("java"))).isEmpty(); + } } -- cgit v1.2.3