summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb28
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java34
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java46
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/CompleteIssueMessageMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/PackageKeysMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/ConvertIssueDebtToMinutesMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/DevelopmentCostMeasuresMigration.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/IssueChangelogMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/NotResolvedIssuesOnRemovedComponentsMigration.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/RequirementMeasuresMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/TechnicalDebtMeasuresMigration.java15
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/IssueActionPlanKeyMigration.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/MeasureDataMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/AddMissingRuleParameterDefaultValuesMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v451/DeleteUnescapedActivities.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedFileSources.java62
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedIssueLongDates.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedSnapshotSourcesUpdatedAt.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/InsertProjectsAuthorizationUpdatedAtMigration.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/RemoveSortFieldFromIssueFiltersMigration.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.java320
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedAnalysisReportsLongDates.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedEventsLongDates.java76
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueChangesLongDates.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueComponentUuids.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueTags.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSemaphoresLongDates.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSnapshotsLongDates.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedUsersLongDates.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/RenameComponentRelatedParamsInIssueFilters.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java14
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java62
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilder.java29
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java32
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java6
-rw-r--r--server/sonar-server/src/main/resources/com/sonar/sqale/technical-debt-model.xml69
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java67
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java55
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest.java131
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest.java88
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/debt/DebtMediumTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyBulkRequestBuilderTest.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilderTest.java5
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorMediumTest.java78
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/do_nothing_when_already_migrated.xml53
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/empty.xml3
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_as_characteristic.xml9
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml13
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_usability_exists_as_sub_characteristic.xml9
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists-result.xml30
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists.xml9
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate-result.xml74
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate.xml27
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml14
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/schema.sql11
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists-result.xml30
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists.xml13
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/before.xml28
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/schema.sql7
-rw-r--r--server/sonar-web/src/main/coffee/issue/issue-view.coffee2
-rw-r--r--server/sonar-web/src/main/coffee/issues/facets/creation-date-facet.coffee8
-rw-r--r--server/sonar-web/src/main/coffee/issues/models/state.coffee4
-rw-r--r--server/sonar-web/src/main/coffee/quality-gate/app.coffee11
-rw-r--r--server/sonar-web/src/main/coffee/quality-gate/layout.coffee44
-rw-r--r--server/sonar-web/src/main/coffee/quality-gate/router.coffee2
-rw-r--r--server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee15
-rw-r--r--server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee2
-rw-r--r--server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee3
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-workspace-list-item.hbs2
-rw-r--r--server/sonar-web/src/main/hbs/issues/issues-filters.hbs21
-rw-r--r--server/sonar-web/src/main/hbs/issues/issues-workspace-header.hbs8
-rw-r--r--server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-condition.hbs8
-rw-r--r--server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-header.hbs24
-rw-r--r--server/sonar-web/src/main/hbs/quality-gates/quality-gate-sidebar-list-item.hbs8
-rw-r--r--server/sonar-web/src/main/hbs/quality-gates/quality-gates-layout.hbs19
-rw-r--r--server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs11
-rw-r--r--server/sonar-web/src/main/js/coding-rules/controller.js16
-rw-r--r--server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js6
-rw-r--r--server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js1
-rw-r--r--server/sonar-web/src/main/js/drilldown/app.js2
-rw-r--r--server/sonar-web/src/main/js/graphics/barchart.js12
-rw-r--r--server/sonar-web/src/main/js/nav/app.js18
-rw-r--r--server/sonar-web/src/main/js/source-viewer/viewer.js27
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/quality-gates-spec.js64
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/views/quality-gates.jade3
-rw-r--r--server/sonar-web/src/main/less/components/issues.less2
-rw-r--r--server/sonar-web/src/main/less/components/navbar.less15
-rw-r--r--server/sonar-web/src/main/less/components/navigator/base.less4
-rw-r--r--server/sonar-web/src/main/less/components/navigator/filters.less4
-rw-r--r--server/sonar-web/src/main/less/components/page.less14
-rw-r--r--server/sonar-web/src/main/less/components/source.less11
-rw-r--r--server/sonar-web/src/main/less/components/ui.less159
-rw-r--r--server/sonar-web/src/main/less/init.less1
-rw-r--r--server/sonar-web/src/main/less/init/forms.less164
-rw-r--r--server/sonar-web/src/main/less/init/type.less4
-rw-r--r--server/sonar-web/src/main/less/pages/quality-gates.less8
-rw-r--r--server/sonar-web/src/main/less/sonar-colorizer.less13
-rw-r--r--server/sonar-web/src/main/less/variables.less2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb5
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb10
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb1
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/models/event.rb25
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/all_projects/index.html.erb66
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/no_dashboard.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb3
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb5
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/nonav.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/maintenance/index.html.erb5
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_body.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_header.html.erb18
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb6
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_groups.html.erb8
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_users.html.erb8
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/db/migrate/791_add_events_long_dates.rb29
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/db/migrate/792_feed_events_long_dates.rb29
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/db/migrate/793_rename_events_long_dates.rb34
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/db/migrate/794_add_characteristic_usability_and_sub_characteristics_compliance.rb31
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java36
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java16
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java35
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java24
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java16
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/Cache.java73
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java55
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java38
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java16
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java19
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java9
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java12
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java37
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java6
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java12
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/CacheTest.java29
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/ResourcePersisterTest.java13
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java49
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java6
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java34
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java3
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java47
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/no-previous-version.xml8
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version-deleted.xml10
-rw-r--r--sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version.xml12
-rw-r--r--sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java36
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java4
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql4
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl4
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties8
-rw-r--r--sonar-core/src/test/java/org/sonar/core/purge/PurgeCommandsTest.java6
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml2
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot-result.xml2
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot.xml4
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot-result.xml8
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot.xml8
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldSelectPurgeableSnapshots.xml2
-rw-r--r--sonar-graph/src/main/java/org/sonar/graph/Dsm.java9
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java25
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java14
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java13
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java23
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/NewDependency.java45
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/License.java10
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java9
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java55
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/config/LicenseTest.java19
195 files changed, 2805 insertions, 1026 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb
index 000f9c35b81..0e645dfa2bd 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb
@@ -43,12 +43,14 @@
new_technical_debt_variation = variation_value(new_technical_debt)
estimated_cleared_technical_debt = (new_technical_debt_variation - technical_debt_variation).to_i if technical_debt_variation && new_technical_debt_variation
%>
- <p class="small">
- <%= message('widget.rules.added') %>
- <a href="<%= url_for_drilldown('new_technical_debt', :period => @dashboard_configuration.period_index) -%>"
- class="varw widget-link widget-link-red link-<%= widget.key %>-new-debt"
- title="<%= tooltip -%>" data-toggle="tooltip" data-placement="bottom">+<%= format_variation(new_technical_debt, :style => 'none', :default => '-') -%></a>
- </p>
+ <% if new_technical_debt_variation && new_technical_debt_variation > 0 %>
+ <p class="small">
+ <%= message('widget.rules.added') %>
+ <a href="<%= url_for_drilldown('new_technical_debt', :period => @dashboard_configuration.period_index) -%>"
+ class="varw widget-link widget-link-red link-<%= widget.key %>-new-debt"
+ title="<%= tooltip -%>" data-toggle="tooltip" data-placement="bottom">+<%= format_variation(new_technical_debt, :style => 'none', :default => '-') -%></a>
+ </p>
+ <% end %>
<% if estimated_cleared_technical_debt && estimated_cleared_technical_debt > 0 %>
<p class="small">
<%= message('widget.rules.removed') %>
@@ -82,12 +84,14 @@
new_issues_variation = variation_value(new_issues)
estimated_cleared_issues = (new_issues_variation - issues_variation).to_i if issues_variation && new_issues_variation
%>
- <p class="small">
- <%= message('widget.rules.added') %>
- <a href="<%= url_for(:controller => 'component_issues', :action => 'index') -%>?id=<%= url_encode(@project.key) -%>#resolved=false|createdAfter=<%= period_date -%>"
- class="varw widget-link widget-link-red link-<%= widget.key %>-new-issues"
- title="<%= tooltip -%>" data-toggle="tooltip" data-placement="bottom">+<%= format_variation(new_issues, :style => 'none', :default => '-') -%></a>
- </p>
+ <% if new_issues_variation && new_issues_variation > 0 %>
+ <p class="small">
+ <%= message('widget.rules.added') %>
+ <a href="<%= url_for(:controller => 'component_issues', :action => 'index') -%>?id=<%= url_encode(@project.key) -%>#resolved=false|createdAfter=<%= period_date -%>"
+ class="varw widget-link widget-link-red link-<%= widget.key %>-new-issues"
+ title="<%= tooltip -%>" data-toggle="tooltip" data-placement="bottom">+<%= format_variation(new_issues, :style => 'none', :default => '-') -%></a>
+ </p>
+ <% end %>
<% if estimated_cleared_issues && estimated_cleared_issues > 0 %>
<p class="small">
<%= message('widget.rules.removed') %>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb
index 18c5d7785a2..55ce78fe4d6 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb
@@ -101,7 +101,7 @@
from_date = first_date if !from_date || from_date > first_date
end
end
- Event.find(:all, :conditions => ["resource_id=? AND event_date>=?", @resource.id, from_date], :order => 'event_date').each() do |event|
+ Event.find(:all, :conditions => ["resource_id=? AND event_date>=?", @resource.id, from_date.to_i*1000], :order => 'event_date').each() do |event|
if events[event.event_date]
events[event.event_date] << event
else
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java
index 5c265dfc5e7..4b8ce4c3b48 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java
@@ -20,6 +20,7 @@
package org.sonar.server.batch;
+import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
@@ -67,7 +68,7 @@ public class ProjectRepositoryLoader implements ServerComponent {
private final Languages languages;
public ProjectRepositoryLoader(DbClient dbClient, QProfileFactory qProfileFactory, QProfileLoader qProfileLoader, RuleService ruleService,
- Languages languages) {
+ Languages languages) {
this.dbClient = dbClient;
this.qProfileFactory = qProfileFactory;
this.qProfileLoader = qProfileLoader;
@@ -86,10 +87,9 @@ public class ProjectRepositoryLoader implements ServerComponent {
ComponentDto module = dbClient.componentDao().getNullableByKey(session, query.getModuleKey());
// Current project/module can be null when analysing a new project
if (module != null) {
- if (query.isPreview()) {
- // Scan permission is enough to analyze all projects but preview permission is limited to projects user can access
- UserSession.get().checkComponentPermission(UserRole.USER, query.getModuleKey(),
- "You're not authorized to access to project '" + module.name() + "', please contact your SonarQube administrator.");
+ // Scan permission is enough to analyze all projects but preview permission is limited to projects user can access
+ if (query.isPreview() && !UserSession.get().hasProjectPermissionByUuid(UserRole.USER, module.projectUuid())) {
+ throw new ForbiddenException("You're not authorized to access to project '" + module.name() + "', please contact your SonarQube administrator.");
}
ComponentDto project = getProject(module, session);
@@ -156,7 +156,7 @@ public class ProjectRepositoryLoader implements ServerComponent {
}
private void addSettingsToChildrenModules(ProjectRepositories ref, String moduleKey, Map<String, String> parentProperties, TreeModuleSettings treeModuleSettings,
- boolean hasScanPerm, DbSession session) {
+ boolean hasScanPerm, DbSession session) {
Map<String, String> currentParentProperties = newHashMap();
currentParentProperties.putAll(parentProperties);
currentParentProperties.putAll(getPropertiesMap(treeModuleSettings.findModuleSettings(moduleKey), hasScanPerm));
@@ -224,12 +224,15 @@ public class ProjectRepositoryLoader implements ServerComponent {
private void addActiveRules(ProjectRepositories ref) {
for (org.sonar.batch.protocol.input.QProfile qProfile : ref.qProfiles()) {
- for (ActiveRule activeRule : qProfileLoader.findActiveRulesByProfile(qProfile.key())) {
- Rule rule = ruleService.getNonNullByKey(activeRule.key().ruleKey());
+ Map<RuleKey, ActiveRule> activeRules = activeRuleByRuleKey(qProfileLoader.findActiveRulesByProfile(qProfile.key()));
+ Iterator<Rule> rules = ruleService.search(new RuleQuery().setQProfileKey(qProfile.key()).setActivation(true), new QueryContext().setScroll(true)).scroll();
+ while (rules.hasNext()) {
+ Rule rule = rules.next();
RuleKey templateKey = rule.templateKey();
+ ActiveRule activeRule = activeRules.get(rule.key());
org.sonar.batch.protocol.input.ActiveRule inputActiveRule = new org.sonar.batch.protocol.input.ActiveRule(
- activeRule.key().ruleKey().repository(),
- activeRule.key().ruleKey().rule(),
+ rule.key().repository(),
+ rule.key().rule(),
templateKey != null ? templateKey.rule() : null,
rule.name(),
activeRule.severity(),
@@ -243,6 +246,15 @@ public class ProjectRepositoryLoader implements ServerComponent {
}
}
+ private Map<RuleKey, ActiveRule> activeRuleByRuleKey(List<ActiveRule> activeRules) {
+ return Maps.uniqueIndex(activeRules, new Function<ActiveRule, RuleKey>() {
+ @Override
+ public RuleKey apply(@Nullable ActiveRule input) {
+ return input != null ? input.key().ruleKey() : null;
+ }
+ });
+ }
+
private void addManualRules(ProjectRepositories ref) {
Result<Rule> ruleSearchResult = ruleService.search(new RuleQuery().setRepositories(newArrayList(RuleKey.MANUAL_REPOSITORY_KEY)), new QueryContext().setScroll(true)
.setFieldsToReturn(newArrayList(RuleNormalizer.RuleField.KEY.field(), RuleNormalizer.RuleField.NAME.field())));
@@ -309,7 +321,7 @@ public class ProjectRepositoryLoader implements ServerComponent {
private Multimap<String, ComponentDto> moduleChildrenByModuleUuid;
private TreeModuleSettings(Map<String, String> moduleUuidsByKey, Map<String, Long> moduleIdsByKey, List<ComponentDto> moduleChildren,
- List<PropertyDto> moduleChildrenSettings, ComponentDto module) {
+ List<PropertyDto> moduleChildrenSettings, ComponentDto module) {
this.moduleIdsByKey = moduleIdsByKey;
this.moduleUuidsByKey = moduleUuidsByKey;
propertiesByModuleId = ArrayListMultimap.create();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
index dd8a452b74c..c2411fa5d4a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java
@@ -90,6 +90,8 @@ public interface DatabaseMigrations {
FeedFileSourcesBinaryData.class,
FeedSemaphoresLongDates.class,
FeedProjectMeasuresLongDates.class,
- FeedManualMeasuresLongDates.class
- );
+ FeedManualMeasuresLongDates.class,
+ FeedEventsLongDates.class,
+ AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.class
+ );
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java
index d9e4cbffa35..4c5b35143df 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/Select.java
@@ -37,46 +37,74 @@ public interface Select extends SqlStatement<Select> {
}
@CheckForNull
- public Long getLong(int columnIndex) throws SQLException {
+ public Long getNullableLong(int columnIndex) throws SQLException {
long l = rs.getLong(columnIndex);
return rs.wasNull() ? null : l;
}
+ public long getLong(int columnIndex) throws SQLException {
+ return rs.getLong(columnIndex);
+ }
+
@CheckForNull
- public Double getDouble(int columnIndex) throws SQLException {
+ public Double getNullableDouble(int columnIndex) throws SQLException {
double d = rs.getDouble(columnIndex);
return rs.wasNull() ? null : d;
}
+ public double getDouble(int columnIndex) throws SQLException {
+ return rs.getDouble(columnIndex);
+ }
+
@CheckForNull
- public Integer getInt(int columnIndex) throws SQLException {
+ public Integer getNullableInt(int columnIndex) throws SQLException {
int i = rs.getInt(columnIndex);
return rs.wasNull() ? null : i;
}
+ public int getInt(int columnIndex) throws SQLException {
+ return rs.getInt(columnIndex);
+ }
+
@CheckForNull
- public Boolean getBoolean(int columnIndex) throws SQLException {
+ public Boolean getNullableBoolean(int columnIndex) throws SQLException {
boolean b = rs.getBoolean(columnIndex);
return rs.wasNull() ? null : b;
}
+ public boolean getBoolean(int columnIndex) throws SQLException {
+ return rs.getBoolean(columnIndex);
+ }
+
@CheckForNull
- public String getString(int columnIndex) throws SQLException {
+ public String getNullableString(int columnIndex) throws SQLException {
String s = rs.getString(columnIndex);
return rs.wasNull() ? null : s;
}
+ public String getString(int columnIndex) throws SQLException {
+ return rs.getString(columnIndex);
+ }
+
@CheckForNull
- public Date getDate(int columnIndex) throws SQLException {
+ public Date getNullableDate(int columnIndex) throws SQLException {
Timestamp t = rs.getTimestamp(columnIndex);
return rs.wasNull() ? null : t;
}
+ public Date getDate(int columnIndex) throws SQLException {
+ return rs.getTimestamp(columnIndex);
+ }
+
@CheckForNull
- public byte[] getBytes(int columnIndex) throws SQLException {
+ public byte[] getNullableBytes(int columnIndex) throws SQLException {
byte[] b = rs.getBytes(columnIndex);
return rs.wasNull() ? null : b;
}
+
+ public byte[] getBytes(int columnIndex) throws SQLException {
+ return rs.getBytes(columnIndex);
+ }
}
static interface RowReader<T> {
@@ -89,7 +117,7 @@ public interface Select extends SqlStatement<Select> {
@Override
public Long read(Row row) throws SQLException {
- return row.getLong(1);
+ return row.getNullableLong(1);
}
}
@@ -101,7 +129,7 @@ public interface Select extends SqlStatement<Select> {
@Override
public String read(Row row) throws SQLException {
- return row.getString(1);
+ return row.getNullableString(1);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/CompleteIssueMessageMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/CompleteIssueMessageMigration.java
index 4cec3c611dc..14259d6b617 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/CompleteIssueMessageMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/CompleteIssueMessageMigration.java
@@ -46,8 +46,8 @@ public class CompleteIssueMessageMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long issueId = row.getLong(1);
- String ruleName = row.getString(2);
+ Long issueId = row.getNullableLong(1);
+ String ruleName = row.getNullableString(2);
update.setString(1, ruleName);
update.setLong(2, issueId);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/PackageKeysMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/PackageKeysMigration.java
index 284b1893cdf..1a2e22bc815 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/PackageKeysMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v42/PackageKeysMigration.java
@@ -50,8 +50,8 @@ public class PackageKeysMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- String key = row.getString(2);
+ Long id = row.getNullableLong(1);
+ String key = row.getNullableString(2);
update.setString(1, convertKey(key));
update.setLong(2, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/ConvertIssueDebtToMinutesMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/ConvertIssueDebtToMinutesMigration.java
index 9d867cf4978..091eb2a7e0d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/ConvertIssueDebtToMinutesMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/ConvertIssueDebtToMinutesMigration.java
@@ -63,9 +63,9 @@ public class ConvertIssueDebtToMinutesMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long debt = row.getLong(2);
+ Long debt = row.getNullableLong(2);
if (debt != null) {
- Long id = row.getLong(1);
+ Long id = row.getNullableLong(1);
update.setLong(1, workDurationConvertor.createFromLong(debt));
update.setDate(2, now);
update.setLong(3, id);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/DevelopmentCostMeasuresMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/DevelopmentCostMeasuresMigration.java
index 08746ac32c1..6345bc53b03 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/DevelopmentCostMeasuresMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/DevelopmentCostMeasuresMigration.java
@@ -28,6 +28,7 @@ import org.sonar.server.db.migrations.Select;
import org.sonar.server.db.migrations.SqlStatement;
import javax.annotation.CheckForNull;
+
import java.sql.SQLException;
/**
@@ -56,8 +57,8 @@ public class DevelopmentCostMeasuresMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Double value = row.getDouble(2);
+ Long id = row.getNullableLong(1);
+ Double value = row.getNullableDouble(2);
update.setString(1, convertDebtForDays(value));
update.setLong(2, id);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/IssueChangelogMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/IssueChangelogMigration.java
index e0a4397e268..de3c557dc93 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/IssueChangelogMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/IssueChangelogMigration.java
@@ -71,8 +71,8 @@ public class IssueChangelogMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- String changeData = row.getString(2);
+ Long id = row.getNullableLong(1);
+ String changeData = row.getNullableString(2);
update.setString(1, convertChangelog(changeData));
update.setDate(2, now);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/NotResolvedIssuesOnRemovedComponentsMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/NotResolvedIssuesOnRemovedComponentsMigration.java
index 6c5c518c893..6c740c6d35b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/NotResolvedIssuesOnRemovedComponentsMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/NotResolvedIssuesOnRemovedComponentsMigration.java
@@ -56,7 +56,7 @@ public class NotResolvedIssuesOnRemovedComponentsMigration extends BaseDataChang
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
+ Long id = row.getNullableLong(1);
update.setString(1, Issue.STATUS_CLOSED);
update.setString(2, Issue.RESOLUTION_REMOVED);
update.setDate(3, now);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/RequirementMeasuresMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/RequirementMeasuresMigration.java
index 02a97f6a8f8..ba68b29e87b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/RequirementMeasuresMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/RequirementMeasuresMigration.java
@@ -49,8 +49,8 @@ public class RequirementMeasuresMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Long ruleId = row.getLong(2);
+ Long id = row.getNullableLong(1);
+ Long ruleId = row.getNullableLong(2);
update.setLong(1, ruleId);
update.setLong(2, id);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/TechnicalDebtMeasuresMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/TechnicalDebtMeasuresMigration.java
index e5bf642069d..2ce40849d72 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/TechnicalDebtMeasuresMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v43/TechnicalDebtMeasuresMigration.java
@@ -30,6 +30,7 @@ import org.sonar.server.db.migrations.SqlStatement;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.sql.SQLException;
import java.util.List;
@@ -78,13 +79,13 @@ public class TechnicalDebtMeasuresMigration extends BaseDataChange {
private class Converter implements MassUpdate.Handler {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Double value = row.getDouble(2);
- Double var1 = row.getDouble(3);
- Double var2 = row.getDouble(4);
- Double var3 = row.getDouble(5);
- Double var4 = row.getDouble(6);
- Double var5 = row.getDouble(7);
+ Long id = row.getNullableLong(1);
+ Double value = row.getNullableDouble(2);
+ Double var1 = row.getNullableDouble(3);
+ Double var2 = row.getNullableDouble(4);
+ Double var3 = row.getNullableDouble(5);
+ Double var4 = row.getNullableDouble(6);
+ Double var5 = row.getNullableDouble(7);
update.setLong(1, convertDebtForDays(value));
update.setLong(2, convertDebtForDays(var1));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java
index e6aaf804c74..90c9ed80ffa 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/FeedQProfileKeysMigration.java
@@ -55,9 +55,9 @@ public class FeedQProfileKeysMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- String lang = row.getString(2);
- String name = row.getString(3);
+ Long id = row.getNullableLong(1);
+ String lang = row.getNullableString(2);
+ String name = row.getNullableString(3);
update.setString(1, Slug.slugify(String.format("%s %s %s", lang, name, RandomStringUtils.randomNumeric(5))));
update.setLong(2, id);
@@ -74,8 +74,8 @@ public class FeedQProfileKeysMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- String parentKey = row.getString(2);
+ Long id = row.getNullableLong(1);
+ String parentKey = row.getNullableString(2);
update.setString(1, parentKey);
update.setLong(2, id);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/IssueActionPlanKeyMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/IssueActionPlanKeyMigration.java
index f15a77b1ff1..9adad2dab5f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/IssueActionPlanKeyMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/IssueActionPlanKeyMigration.java
@@ -59,7 +59,7 @@ public class IssueActionPlanKeyMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
+ Long id = row.getNullableLong(1);
update.setDate(1, now);
update.setLong(2, id);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/MeasureDataMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/MeasureDataMigration.java
index 8e8a0dc8421..23ecb4c450b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/MeasureDataMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/MeasureDataMigration.java
@@ -51,8 +51,8 @@ public class MeasureDataMigration extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Long measureId = row.getLong(2);
+ Long id = row.getNullableLong(1);
+ Long measureId = row.getNullableLong(2);
update.setLong(1, id);
update.setLong(2, measureId);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/AddMissingRuleParameterDefaultValuesMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/AddMissingRuleParameterDefaultValuesMigration.java
index dbe21119526..02b02f8d727 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/AddMissingRuleParameterDefaultValuesMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v45/AddMissingRuleParameterDefaultValuesMigration.java
@@ -50,7 +50,7 @@ public class AddMissingRuleParameterDefaultValuesMigration extends BaseDataChang
.list(new Select.RowReader<RuleParam>() {
@Override
public RuleParam read(Select.Row row) throws SQLException {
- return new RuleParam(row.getLong(1), row.getLong(2), row.getString(3), row.getString(4));
+ return new RuleParam(row.getNullableLong(1), row.getNullableLong(2), row.getNullableString(3), row.getNullableString(4));
}
});
@@ -63,7 +63,7 @@ public class AddMissingRuleParameterDefaultValuesMigration extends BaseDataChang
.list(new Select.RowReader<ActiveRule>() {
@Override
public ActiveRule read(Select.Row row) throws SQLException {
- return new ActiveRule(row.getLong(1), row.getLong(2));
+ return new ActiveRule(row.getNullableLong(1), row.getNullableLong(2));
}
});
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v451/DeleteUnescapedActivities.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v451/DeleteUnescapedActivities.java
index 87e0566b593..49c50bc0980 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v451/DeleteUnescapedActivities.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v451/DeleteUnescapedActivities.java
@@ -49,9 +49,9 @@ public class DeleteUnescapedActivities extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- String csv = row.getString(2);
+ String csv = row.getNullableString(2);
if (isUnescaped(csv)) {
- update.setLong(1, row.getLong(1));
+ update.setLong(1, row.getNullableLong(1));
return true;
}
return false;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedFileSources.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedFileSources.java
index 12bf122af39..fc6aa2d0f2c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedFileSources.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedFileSources.java
@@ -150,36 +150,36 @@ public class FeedFileSources extends BaseDataChange {
@Override
public boolean handle(Row row, SqlStatement update) throws SQLException {
- String projectUuid = row.getString(1);
- String fileUuid = row.getString(2);
- String source = StringUtils.defaultIfBlank(row.getString(3), "");
- Date updatedAt = row.getDate(4);
- byte[] shortRevisions = row.getBytes(5);
- byte[] longRevisions = row.getBytes(6);
- byte[] shortAuthors = row.getBytes(7);
- byte[] longAuthors = row.getBytes(8);
- byte[] shortDates = row.getBytes(9);
- byte[] longDates = row.getBytes(10);
- byte[] shortUtHits = row.getBytes(11);
- byte[] longUtHits = row.getBytes(12);
- byte[] shortUtCond = row.getBytes(13);
- byte[] longUtCond = row.getBytes(14);
- byte[] shortUtCovCond = row.getBytes(15);
- byte[] longUtCovCond = row.getBytes(16);
- byte[] shortItHits = row.getBytes(17);
- byte[] longItHits = row.getBytes(18);
- byte[] shortItCond = row.getBytes(19);
- byte[] longItCond = row.getBytes(20);
- byte[] shortItCovCond = row.getBytes(21);
- byte[] longItCovCond = row.getBytes(22);
- byte[] shortOverallHits = row.getBytes(23);
- byte[] longOverallHits = row.getBytes(24);
- byte[] shortOverallCond = row.getBytes(25);
- byte[] longOverallCond = row.getBytes(26);
- byte[] shortOverallCovCond = row.getBytes(27);
- byte[] longOverallCovCond = row.getBytes(28);
- byte[] shortDuplicationData = row.getBytes(29);
- byte[] longDuplicationData = row.getBytes(30);
+ String projectUuid = row.getNullableString(1);
+ String fileUuid = row.getNullableString(2);
+ String source = StringUtils.defaultIfBlank(row.getNullableString(3), "");
+ Date updatedAt = row.getNullableDate(4);
+ byte[] shortRevisions = row.getNullableBytes(5);
+ byte[] longRevisions = row.getNullableBytes(6);
+ byte[] shortAuthors = row.getNullableBytes(7);
+ byte[] longAuthors = row.getNullableBytes(8);
+ byte[] shortDates = row.getNullableBytes(9);
+ byte[] longDates = row.getNullableBytes(10);
+ byte[] shortUtHits = row.getNullableBytes(11);
+ byte[] longUtHits = row.getNullableBytes(12);
+ byte[] shortUtCond = row.getNullableBytes(13);
+ byte[] longUtCond = row.getNullableBytes(14);
+ byte[] shortUtCovCond = row.getNullableBytes(15);
+ byte[] longUtCovCond = row.getNullableBytes(16);
+ byte[] shortItHits = row.getNullableBytes(17);
+ byte[] longItHits = row.getNullableBytes(18);
+ byte[] shortItCond = row.getNullableBytes(19);
+ byte[] longItCond = row.getNullableBytes(20);
+ byte[] shortItCovCond = row.getNullableBytes(21);
+ byte[] longItCovCond = row.getNullableBytes(22);
+ byte[] shortOverallHits = row.getNullableBytes(23);
+ byte[] longOverallHits = row.getNullableBytes(24);
+ byte[] shortOverallCond = row.getNullableBytes(25);
+ byte[] longOverallCond = row.getNullableBytes(26);
+ byte[] shortOverallCovCond = row.getNullableBytes(27);
+ byte[] longOverallCovCond = row.getNullableBytes(28);
+ byte[] shortDuplicationData = row.getNullableBytes(29);
+ byte[] longDuplicationData = row.getNullableBytes(30);
String[] sourceData = new FileSourceDto(source,
ofNullableBytes(shortRevisions, longRevisions),
@@ -235,7 +235,7 @@ public class FeedFileSources extends BaseDataChange {
RowReader<Long> simpleLongReader = new RowReader<Long>() {
@Override
public Long read(Row row) throws SQLException {
- Long longValue = row.getLong(1);
+ Long longValue = row.getNullableLong(1);
return longValue == null ? Long.valueOf(0L) : longValue;
}
};
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedIssueLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedIssueLongDates.java
index c97938f721d..9b4dfca0e31 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedIssueLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedIssueLongDates.java
@@ -49,9 +49,9 @@ public class FeedIssueLongDates extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Date createdAt = row.getDate(2);
- Date updatedAt = row.getDate(3);
+ Long id = row.getNullableLong(1);
+ Date createdAt = row.getNullableDate(2);
+ Date updatedAt = row.getNullableDate(3);
if (createdAt == null) {
update.setLong(1, now);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedSnapshotSourcesUpdatedAt.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedSnapshotSourcesUpdatedAt.java
index 3c6df32cfd1..589c080501e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedSnapshotSourcesUpdatedAt.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/FeedSnapshotSourcesUpdatedAt.java
@@ -52,8 +52,8 @@ public class FeedSnapshotSourcesUpdatedAt extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- update.setDate(1, row.getDate(2));
- update.setLong(2, row.getLong(1));
+ update.setDate(1, row.getNullableDate(2));
+ update.setLong(2, row.getNullableLong(1));
return true;
}
});
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/InsertProjectsAuthorizationUpdatedAtMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/InsertProjectsAuthorizationUpdatedAtMigration.java
index 026f52a1bda..c4688c28cb6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/InsertProjectsAuthorizationUpdatedAtMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/InsertProjectsAuthorizationUpdatedAtMigration.java
@@ -54,7 +54,7 @@ public class InsertProjectsAuthorizationUpdatedAtMigration extends BaseDataChang
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
+ Long id = row.getNullableLong(1);
update.setLong(1, now);
update.setLong(2, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/RemoveSortFieldFromIssueFiltersMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/RemoveSortFieldFromIssueFiltersMigration.java
index 64d6dc0f4e9..0f8c7f5ab60 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/RemoveSortFieldFromIssueFiltersMigration.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/RemoveSortFieldFromIssueFiltersMigration.java
@@ -65,7 +65,7 @@ public class RemoveSortFieldFromIssueFiltersMigration extends BaseDataChange {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- String data = row.getString(2);
+ String data = row.getNullableString(2);
String[] fields = StringUtils.split(data, FIELD_SEPARATOR);
boolean found = false;
@@ -81,7 +81,7 @@ public class RemoveSortFieldFromIssueFiltersMigration extends BaseDataChange {
// data without 'sort' field
update.setString(1, StringUtils.join(fieldsToKeep, FIELD_SEPARATOR));
update.setDate(2, now);
- update.setLong(3, row.getLong(1));
+ update.setLong(3, row.getNullableLong(1));
}
return found;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java
index fecf0be4e83..2632f3b27c7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v50/ReplaceIssueFiltersProjectKeyByUuid.java
@@ -64,8 +64,8 @@ public class ReplaceIssueFiltersProjectKeyByUuid extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- String data = row.getString(2);
+ Long id = row.getNullableLong(1);
+ String data = row.getNullableString(2);
if (data == null) {
return false;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.java
new file mode 100644
index 00000000000..60946cef6d4
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.java
@@ -0,0 +1,320 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations.v51;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.Database;
+import org.sonar.server.db.migrations.BaseDataChange;
+import org.sonar.server.db.migrations.Select;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * See http://jira.codehaus.org/browse/SONAR-6187
+ *
+ * Add a new Characteristic 'Usability' with 2 sub-characteristics 'Accessibility' and 'Ease of Use'
+ * and add a new sub-characteristic 'Compliance' for all characteristics.
+ *
+ * Nothing will be done if there's no characteristics in db, as they're all gonna be created by {@link org.sonar.server.startup.RegisterDebtModel}
+ *
+ * Before 4.3 the characteristics table contains requirements, then when selecting characteristics we should not forget to exclude them (with a filter on rule_id IS NULL)
+ *
+ */
+public class AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration extends BaseDataChange {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.class);
+
+ private static final String COMPLIANCE_NAME = "Compliance";
+ private static final String COMPLIANCE_KEY_SUFFIX = "_COMPLIANCE";
+
+ private static final String SECURITY_KEY = "SECURITY";
+ private static final String USABILITY_KEY = "USABILITY";
+
+ private final System2 system;
+
+ public AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ CharacteristicsContext characteristicsContext = new CharacteristicsContext(context, system);
+
+ // On an empty DB, there are no characteristics, they're all gonna be created after in RegisterDebtModel
+ if (!characteristicsContext.characteristics().isEmpty()) {
+ int usabilityOder = moveCharacteristicsDownToBeAbleToInsertUsability(characteristicsContext);
+ createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(characteristicsContext, usabilityOder);
+
+ createSubCharacteristic(characteristicsContext, "REUSABILITY" + COMPLIANCE_KEY_SUFFIX, "Reusability " + COMPLIANCE_NAME, "REUSABILITY");
+ createSubCharacteristic(characteristicsContext, "PORTABILITY" + COMPLIANCE_KEY_SUFFIX, "Portability " + COMPLIANCE_NAME, "PORTABILITY");
+ createSubCharacteristic(characteristicsContext, "MAINTAINABILITY" + COMPLIANCE_KEY_SUFFIX, "Maintainability " + COMPLIANCE_NAME, "MAINTAINABILITY");
+ createSubCharacteristic(characteristicsContext, SECURITY_KEY + COMPLIANCE_KEY_SUFFIX, "Security " + COMPLIANCE_NAME, SECURITY_KEY);
+ createSubCharacteristic(characteristicsContext, "EFFICIENCY" + COMPLIANCE_KEY_SUFFIX, "Efficiency " + COMPLIANCE_NAME, "EFFICIENCY");
+ createSubCharacteristic(characteristicsContext, "CHANGEABILITY" + COMPLIANCE_KEY_SUFFIX, "Changeability " + COMPLIANCE_NAME, "CHANGEABILITY");
+ createSubCharacteristic(characteristicsContext, "RELIABILITY" + COMPLIANCE_KEY_SUFFIX, "Reliability " + COMPLIANCE_NAME, "RELIABILITY");
+ createSubCharacteristic(characteristicsContext, "TESTABILITY" + COMPLIANCE_KEY_SUFFIX, "Testability " + COMPLIANCE_NAME, "TESTABILITY");
+ }
+ }
+
+ /**
+ * If the characteristic 'Security' exists, the new characteristic 'Usability' should be inserted just below it,
+ * so every existing characteristics below Security should move down.
+ *
+ * If the characteristic 'Security' does not exists, the new characteristic 'Usability' should be the first one,
+ * so every existing characteristics should move down.
+ *
+ * If the characteristic 'Usability' is already at the right place, nothing will be done.
+ */
+ private int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException {
+ Characteristic security = characteristicsContext.findCharacteristicByKey(SECURITY_KEY);
+ Characteristic usability = characteristicsContext.findCharacteristicByKey(USABILITY_KEY);
+
+ int usabilityOder = 1;
+ int indexToStart = 0;
+ if (security != null) {
+ indexToStart = characteristicsContext.characteristics().indexOf(security) + 1;
+ usabilityOder = security.getOrder() + 1;
+ }
+
+ if (usability == null || usability.getOrder() != usabilityOder) {
+ // Move root characteristics one step lower
+ for (int i = indexToStart; i < characteristicsContext.characteristics().size(); i++) {
+ Characteristic characteristic = characteristicsContext.characteristics().get(i);
+ if (characteristic.getParentId() == null) {
+ characteristicsContext.updateCharacteristicOrder(characteristic.getKey(), characteristic.getOrder() + 1);
+ }
+ }
+ }
+ return usabilityOder;
+ }
+
+ private void createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(CharacteristicsContext characteristicsContext, int newUsabilityOrder)
+ throws SQLException {
+ String usabilityKey = USABILITY_KEY;
+ Characteristic usability = characteristicsContext.findCharacteristicByKey(usabilityKey);
+ if (usability != null) {
+ if (usability.getOrder() != newUsabilityOrder) {
+ usability.setOrder(newUsabilityOrder);
+ characteristicsContext.updateCharacteristicOrder(usability.getKey(), usability.getOrder());
+ }
+ } else {
+ usability = new Characteristic().setKey(usabilityKey).setName("Usability").setOrder(newUsabilityOrder);
+ characteristicsContext.insertCharacteristic(usability);
+ }
+
+ createSubCharacteristic(characteristicsContext, "USABILITY_ACCESSIBILITY", "Accessibility", usabilityKey);
+ createSubCharacteristic(characteristicsContext, "USABILITY_EASE_OF_USE", "Ease of Use", usabilityKey);
+ createSubCharacteristic(characteristicsContext, USABILITY_KEY + COMPLIANCE_KEY_SUFFIX, "Usability " + COMPLIANCE_NAME, usabilityKey);
+ }
+
+ private void createSubCharacteristic(CharacteristicsContext characteristicsContext,
+ String subCharacteristicKey, String subCharacteristicName, String parentKey) throws SQLException {
+ Characteristic parent = characteristicsContext.findCharacteristicByKey(parentKey);
+ if (parent != null) {
+ Characteristic subCharacteristic = characteristicsContext.findSubCharacteristicByKey(subCharacteristicKey, parent);
+ if (subCharacteristic == null) {
+ characteristicsContext.insertCharacteristic(new Characteristic().setKey(subCharacteristicKey).setName(subCharacteristicName).setParentId(parent.getId()));
+ }
+ }
+ // If the characteristic parent does not exits, the sub-characteristic is not added
+ }
+
+ private static class Characteristic {
+ private Integer id;
+ private String key;
+ private String name;
+ private Integer order;
+ private Integer parentId;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public Characteristic setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Characteristic setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Characteristic setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * On a characteristic, the order can never be null
+ */
+ public Integer getOrder() {
+ return parentId == null && order != null ? order : null;
+ }
+
+ public Characteristic setOrder(@Nullable Integer order) {
+ this.order = order;
+ return this;
+ }
+
+ @CheckForNull
+ public Integer getParentId() {
+ return parentId;
+ }
+
+ public Characteristic setParentId(@Nullable Integer parentId) {
+ this.parentId = parentId;
+ return this;
+ }
+ }
+
+ private static class CharacteristicsContext {
+ private final System2 system;
+ Context context;
+ Date now;
+ List<Characteristic> characteristics;
+
+ public CharacteristicsContext(Context context, System2 system) throws SQLException {
+ this.context = context;
+ this.system = system;
+ init();
+ }
+
+ private void init() throws SQLException {
+ now = new Date(system.now());
+ characteristics = selectEnabledCharacteristics();
+ }
+
+ public List<Characteristic> characteristics() {
+ return characteristics;
+ }
+
+ @CheckForNull
+ public Characteristic findCharacteristicByKey(final String key) {
+ Characteristic characteristic = Iterables.find(characteristics, new Predicate<Characteristic>() {
+ @Override
+ public boolean apply(@Nullable Characteristic input) {
+ return input != null && input.key.equals(key);
+ }
+ }, null);
+ if (characteristic != null && characteristic.getParentId() != null) {
+ throw new IllegalStateException(String.format("'%s' must be a characteristic", characteristic.getName()));
+ }
+ return characteristic;
+ }
+
+ @CheckForNull
+ public Characteristic findSubCharacteristicByKey(final String key, Characteristic parent) {
+ Characteristic characteristic = Iterables.find(characteristics, new Predicate<Characteristic>() {
+ @Override
+ public boolean apply(@Nullable Characteristic input) {
+ return input != null && input.key.equals(key);
+ }
+ }, null);
+ if (characteristic != null) {
+ Integer parentId = characteristic.getParentId();
+ if (parentId == null) {
+ throw new IllegalStateException(String.format("'%s' must be a sub-characteristic", characteristic.getName()));
+ } else if (!parentId.equals(parent.getId())) {
+ throw new IllegalStateException(String.format("'%s' must be defined under '%s'", characteristic.getName(), parent.getName()));
+ }
+ }
+ return characteristic;
+ }
+
+ private List<Characteristic> selectEnabledCharacteristics() throws SQLException {
+ return context.prepareSelect(
+ // Exclude requirements (to not fail when coming from a version older than 4.3)
+ "SELECT c.id, c.kee, c.name, c.characteristic_order, c.parent_id FROM characteristics c WHERE c.enabled=? AND c.rule_id IS NULL ORDER BY c.characteristic_order")
+ .setBoolean(1, true)
+ .list(new CharacteristicReader());
+ }
+
+ private int selectCharacteristicId(String key) throws SQLException {
+ return context.prepareSelect(
+ "SELECT c.id FROM characteristics c WHERE c.kee = ? AND c.enabled=?")
+ .setString(1, key)
+ .setBoolean(2, true)
+ .get(Select.LONG_READER).intValue();
+ }
+
+ public void insertCharacteristic(Characteristic characteristic) throws SQLException {
+ if (characteristic.getParentId() == null) {
+ LOGGER.info("Insert new characteristic '{}'", characteristic.getKey());
+ } else {
+ LOGGER.info("Insert new sub characteristic '{}'", characteristic.getKey());
+ }
+
+ context.prepareUpsert("INSERT INTO characteristics (kee, name, parent_id, characteristic_order, enabled, created_at) VALUES (?, ?, ?, ?, ?, ?)")
+ .setString(1, characteristic.getKey())
+ .setString(2, characteristic.getName())
+ .setInt(3, characteristic.getParentId())
+ .setInt(4, characteristic.getOrder())
+ .setBoolean(5, true)
+ .setDate(6, now)
+ .execute()
+ .commit();
+ characteristic.setId(selectCharacteristicId(characteristic.getKey()));
+
+ characteristics.add(characteristic);
+ }
+
+ public void updateCharacteristicOrder(String key, Integer order) throws SQLException {
+ LOGGER.info("Update characteristic '{}' order to {}", key, order);
+
+ context.prepareUpsert("UPDATE characteristics SET characteristic_order=?, updated_at=? WHERE kee=?")
+ .setInt(1, order)
+ .setDate(2, now)
+ .setString(3, key)
+ .execute()
+ .commit();
+ }
+
+ private static class CharacteristicReader implements Select.RowReader<Characteristic> {
+ @Override
+ public Characteristic read(Select.Row row) throws SQLException {
+ return new Characteristic()
+ .setId(row.getInt(1))
+ .setKey(row.getString(2))
+ .setName(row.getString(3))
+ .setOrder(row.getNullableInt(4))
+ .setParentId(row.getNullableInt(5));
+ }
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java
index e6ebec997f6..86a1ce33a1f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java
@@ -66,7 +66,7 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
).scroll(new Select.RowHandler() {
@Override
public void handle(Select.Row row) throws SQLException {
- authorsByPersonId.put(row.getLong(1), row.getString(2));
+ authorsByPersonId.put(row.getNullableLong(1), row.getNullableString(2));
}
});
@@ -126,7 +126,7 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
select.scroll(new Select.RowHandler() {
@Override
public void handle(Select.Row row) throws SQLException {
- users.add(new User(row.getLong(1), row.getString(2), row.getString(3), row.getString(4)));
+ users.add(new User(row.getNullableLong(1), row.getNullableString(2), row.getNullableString(3), row.getNullableString(4)));
}
});
return users;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedAnalysisReportsLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedAnalysisReportsLongDates.java
index 42c5a6564a1..a0c981df110 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedAnalysisReportsLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedAnalysisReportsLongDates.java
@@ -50,11 +50,11 @@ public class FeedAnalysisReportsLongDates extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Date createdAt = row.getDate(1);
- Date updatedAt = row.getDate(2);
- Date startedAt = row.getDate(3);
- Date finishedAt = row.getDate(4);
- Long id = row.getLong(5);
+ Date createdAt = row.getNullableDate(1);
+ Date updatedAt = row.getNullableDate(2);
+ Date startedAt = row.getNullableDate(3);
+ Date finishedAt = row.getNullableDate(4);
+ Long id = row.getNullableLong(5);
update.setLong(1, createdAt == null ? now : Math.min(now, createdAt.getTime()));
update.setLong(2, updatedAt == null ? now : Math.min(now, updatedAt.getTime()));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedEventsLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedEventsLongDates.java
new file mode 100644
index 00000000000..f7a5ef9332a
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedEventsLongDates.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.db.migrations.v51;
+
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.Database;
+import org.sonar.server.db.migrations.BaseDataChange;
+import org.sonar.server.db.migrations.MassUpdate;
+import org.sonar.server.db.migrations.Select;
+import org.sonar.server.db.migrations.SqlStatement;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+public class FeedEventsLongDates extends BaseDataChange {
+
+ private final System2 system2;
+
+ public FeedEventsLongDates(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate
+ .select("SELECT e.event_date, e.created_at, e.id FROM events e WHERE event_date_ms IS NULL");
+ massUpdate
+ .update("UPDATE events SET event_date_ms=?, created_at_ms=? WHERE id=?");
+ massUpdate.rowPluralName("events");
+ massUpdate.execute(new EventDateHandler(system2.now()));
+ }
+
+ private static class EventDateHandler implements MassUpdate.Handler {
+
+ private final long now;
+
+ public EventDateHandler(long now) {
+ this.now = now;
+ }
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Date eventDate = row.getNullableDate(1);
+ long eventTime = eventDate == null ? now : Math.min(now, eventDate.getTime());
+ update.setLong(1, eventTime);
+ Date createdAt = row.getNullableDate(2);
+ update.setLong(2, createdAt == null ? eventTime : Math.min(now, createdAt.getTime()));
+
+ Long id = row.getNullableLong(3);
+ update.setLong(3, id);
+
+ return true;
+ }
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java
index ec57d336812..00d1d8afd6c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java
@@ -38,6 +38,7 @@ import org.sonar.server.source.db.FileSourceDb;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+
import java.sql.SQLException;
import java.util.Iterator;
@@ -55,8 +56,8 @@ public class FeedFileSourcesBinaryData extends BaseDataChange {
update.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long fileSourceId = row.getLong(1);
- update.setBytes(1, toBinary(fileSourceId, row.getString(2)));
+ Long fileSourceId = row.getNullableLong(1);
+ update.setBytes(1, toBinary(fileSourceId, row.getNullableString(2)));
update.setLong(2, fileSourceId);
return true;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueChangesLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueChangesLongDates.java
index 259e82b0060..d522dae4ade 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueChangesLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueChangesLongDates.java
@@ -50,10 +50,10 @@ public class FeedIssueChangesLongDates extends BaseDataChange {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Date createdAt = row.getDate(1);
- Date updatedAt = row.getDate(2);
- Date functionalCreatedAt = row.getDate(3);
- Long id = row.getLong(4);
+ Date createdAt = row.getNullableDate(1);
+ Date updatedAt = row.getNullableDate(2);
+ Date functionalCreatedAt = row.getNullableDate(3);
+ Long id = row.getNullableLong(4);
update.setLong(1, createdAt == null ? now : Math.min(now, createdAt.getTime()));
update.setLong(2, updatedAt == null ? now : Math.min(now, updatedAt.getTime()));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueComponentUuids.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueComponentUuids.java
index 8e07193b67c..7c26de3a13a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueComponentUuids.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueComponentUuids.java
@@ -47,9 +47,9 @@ public class FeedIssueComponentUuids extends BaseDataChange {
update.execute(new Handler() {
@Override
public boolean handle(Row row, SqlStatement update) throws SQLException {
- update.setString(1, row.getString(1));
- update.setString(2, row.getString(2));
- update.setLong(3, row.getLong(3));
+ update.setString(1, row.getNullableString(1));
+ update.setString(2, row.getNullableString(2));
+ update.setLong(3, row.getNullableLong(3));
return true;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueTags.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueTags.java
index cd38aa0741e..5eaf90f8f48 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueTags.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssueTags.java
@@ -56,10 +56,10 @@ public class FeedIssueTags extends BaseDataChange {
context.prepareSelect("SELECT id, system_tags, tags FROM rules").scroll(new RowHandler() {
@Override
public void handle(Row row) throws SQLException {
- Integer id = row.getInt(1);
+ Integer id = row.getNullableInt(1);
tagsByRuleId.put(id, StringUtils.trimToNull(TAG_JOINER.join(
- StringUtils.trimToNull(row.getString(2)),
- StringUtils.trimToNull(row.getString(3)))));
+ StringUtils.trimToNull(row.getNullableString(2)),
+ StringUtils.trimToNull(row.getNullableString(3)))));
}
});
@@ -69,8 +69,8 @@ public class FeedIssueTags extends BaseDataChange {
update.execute(new Handler() {
@Override
public boolean handle(Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Integer ruleId = row.getInt(2);
+ Long id = row.getNullableLong(1);
+ Integer ruleId = row.getNullableInt(2);
boolean updated = false;
if (tagsByRuleId.get(ruleId) != null) {
updated = true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java
index 5734005ed6f..81a690a38e9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java
@@ -51,10 +51,10 @@ public class FeedIssuesLongDates extends BaseDataChange {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
for (int i = 1; i <= 3; i++) {
- update.setLong(i, row.getDate(i) == null ? null : Math.min(now, row.getDate(i).getTime()));
+ update.setLong(i, row.getNullableDate(i) == null ? null : Math.min(now, row.getNullableDate(i).getTime()));
}
- Long id = row.getLong(4);
+ Long id = row.getNullableLong(4);
update.setLong(4, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java
index 78529fcfa6c..d127b7f8f71 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java
@@ -47,16 +47,16 @@ public class FeedManualMeasuresLongDates extends BaseDataChange {
.select("SELECT m.created_at, m.updated_at, m.id FROM manual_measures m WHERE created_at_ms IS NULL");
massUpdate
.update("UPDATE manual_measures SET created_at_ms=?, updated_at_ms=? WHERE id=?");
- massUpdate.rowPluralName("manualMeasures");
+ massUpdate.rowPluralName("manual measures");
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
for (int i = 1; i <= 2; i++) {
- Date date = row.getDate(i);
+ Date date = row.getNullableDate(i);
update.setLong(i, date == null ? null : Math.min(now, date.getTime()));
}
- Long id = row.getLong(3);
+ Long id = row.getNullableLong(3);
update.setLong(3, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java
index 6cea58309dc..dce97a78d7d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java
@@ -47,14 +47,14 @@ public class FeedProjectMeasuresLongDates extends BaseDataChange {
.select("SELECT m.measure_date, m.id FROM project_measures m WHERE measure_date_ms IS NULL");
massUpdate
.update("UPDATE project_measures SET measure_date_ms=? WHERE id=?");
- massUpdate.rowPluralName("projectMeasures");
+ massUpdate.rowPluralName("project measures");
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Date date = row.getDate(1);
+ Date date = row.getNullableDate(1);
update.setLong(1, date == null ? null : Math.min(now, date.getTime()));
- Long id = row.getLong(2);
+ Long id = row.getNullableLong(2);
update.setLong(2, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSemaphoresLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSemaphoresLongDates.java
index 1ddc3a03c71..bf4ddd08419 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSemaphoresLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSemaphoresLongDates.java
@@ -52,11 +52,11 @@ public class FeedSemaphoresLongDates extends BaseDataChange {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
for (int i = 1; i <= 3; i++) {
- Date date = row.getDate(i);
+ Date date = row.getNullableDate(i);
update.setLong(i, date == null ? null : Math.min(now, date.getTime()));
}
- Long id = row.getLong(4);
+ Long id = row.getNullableLong(4);
update.setLong(4, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSnapshotsLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSnapshotsLongDates.java
index 09998ea93eb..2897eb7dd1d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSnapshotsLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedSnapshotsLongDates.java
@@ -51,10 +51,10 @@ public class FeedSnapshotsLongDates extends BaseDataChange {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
for (int i = 1; i <= 7; i++) {
- update.setLong(i, row.getDate(i) == null ? null : Math.min(now, row.getDate(i).getTime()));
+ update.setLong(i, row.getNullableDate(i) == null ? null : Math.min(now, row.getNullableDate(i).getTime()));
}
- Long id = row.getLong(8);
+ Long id = row.getNullableLong(8);
update.setLong(8, id);
return true;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedUsersLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedUsersLongDates.java
index 5324120c6e3..3907ddf94b1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedUsersLongDates.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedUsersLongDates.java
@@ -57,9 +57,9 @@ public class FeedUsersLongDates extends BaseDataChange {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- Date createdAt = row.getDate(2);
- Date updatedAt = row.getDate(3);
+ Long id = row.getNullableLong(1);
+ Date createdAt = row.getNullableDate(2);
+ Date updatedAt = row.getNullableDate(3);
if (createdAt == null) {
update.setLong(1, now);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/RenameComponentRelatedParamsInIssueFilters.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/RenameComponentRelatedParamsInIssueFilters.java
index b74b5ac3d97..e7862b474c6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/RenameComponentRelatedParamsInIssueFilters.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/RenameComponentRelatedParamsInIssueFilters.java
@@ -68,7 +68,7 @@ public class RenameComponentRelatedParamsInIssueFilters extends BaseDataChange {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- String data = row.getString(2);
+ String data = row.getNullableString(2);
String[] fields = StringUtils.split(data, FIELD_SEPARATOR);
List<String> fieldsToKeep = Lists.newArrayList();
@@ -83,7 +83,7 @@ public class RenameComponentRelatedParamsInIssueFilters extends BaseDataChange {
}
update.setString(1, StringUtils.join(fieldsToKeep, FIELD_SEPARATOR));
update.setDate(2, now);
- update.setLong(3, row.getLong(1));
+ update.setLong(3, row.getNullableLong(1));
return true;
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java
index 28fe41810ff..751926fb06b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java
@@ -22,9 +22,11 @@ package org.sonar.server.db.migrations.v51;
import org.apache.commons.lang.StringUtils;
import org.sonar.core.persistence.Database;
-import org.sonar.server.db.migrations.*;
+import org.sonar.server.db.migrations.BaseDataChange;
+import org.sonar.server.db.migrations.MassUpdate;
import org.sonar.server.db.migrations.MassUpdate.Handler;
import org.sonar.server.db.migrations.Select.Row;
+import org.sonar.server.db.migrations.SqlStatement;
import javax.annotation.Nullable;
@@ -53,11 +55,11 @@ public class UpdateProjectsModuleUuidPath extends BaseDataChange {
private static final class ModuleUuidPathUpdateHandler implements Handler {
@Override
public boolean handle(Row row, SqlStatement update) throws SQLException {
- Long id = row.getLong(1);
- String moduleUuidPath = row.getString(2);
- String uuid = row.getString(3);
- String scope = row.getString(4);
- String qualifier = row.getString(5);
+ Long id = row.getNullableLong(1);
+ String moduleUuidPath = row.getNullableString(2);
+ String uuid = row.getNullableString(3);
+ String scope = row.getNullableString(4);
+ String qualifier = row.getNullableString(5);
boolean needUpdate = false;
String newModuleUuidPath = moduleUuidPath;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
index 480e2b74915..66aee7b890e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyBulkRequestBuilder.java
@@ -20,6 +20,10 @@
package org.sonar.server.es.request;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset.Entry;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
@@ -32,6 +36,8 @@ import org.elasticsearch.common.unit.TimeValue;
import org.sonar.core.profiling.Profiling;
import org.sonar.core.profiling.StopWatch;
+import java.util.Set;
+
public class ProxyBulkRequestBuilder extends BulkRequestBuilder {
private final Profiling profiling;
@@ -79,23 +85,71 @@ public class ProxyBulkRequestBuilder extends BulkRequestBuilder {
public String toString() {
StringBuilder message = new StringBuilder();
message.append("Bulk[");
+ HashMultiset<BulkRequestKey> groupedRequests = HashMultiset.create();
for (int i=0 ; i<request.requests().size() ; i++) {
ActionRequest item = request.requests().get(i);
+ String requestType, index, docType;
if (item instanceof IndexRequest) {
IndexRequest request = (IndexRequest) item;
- message.append(String.format("index id '%s' in %s/%s", request.id(), request.index(), request.type()));
+ requestType = "index";
+ index = request.index();
+ docType = request.type();
} else if (item instanceof UpdateRequest) {
UpdateRequest request = (UpdateRequest) item;
- message.append(String.format("update id '%s' in %s/%s", request.id(), request.index(), request.type()));
+ requestType = "update";
+ index = request.index();
+ docType = request.type();
} else if (item instanceof DeleteRequest) {
DeleteRequest request = (DeleteRequest) item;
- message.append(String.format("delete id '%s' from %s/%s", request.id(), request.index(), request.type()));
+ requestType = "delete";
+ index = request.index();
+ docType = request.type();
+ } else {
+ // Cannot happen, not allowed by BulkRequest's contract
+ throw new IllegalStateException("Unsupported bulk request type: " + item.getClass());
}
- if (i < request.requests().size()-1) {
+ groupedRequests.add(new BulkRequestKey(requestType, index, docType));
+ }
+
+ Set<Entry<BulkRequestKey>> entrySet = groupedRequests.entrySet();
+ int size = entrySet.size();
+ int current = 0;
+ for (Entry<BulkRequestKey> requestEntry : entrySet) {
+ message.append(requestEntry.getCount()).append(" ").append(requestEntry.getElement().toString());
+ current++;
+ if (current < size) {
message.append(", ");
}
}
+
message.append("]");
return message.toString();
}
+
+ private static class BulkRequestKey {
+ private String requestType;
+ private String index;
+ private String docType;
+
+ private BulkRequestKey(String requestType, String index, String docType) {
+ this.requestType = requestType;
+ this.index = index;
+ this.docType = docType;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s request(s) on index %s and type %s", requestType, index, docType);
+ }
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilder.java
index f473fc305b2..0d1474e66ff 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilder.java
@@ -26,13 +26,21 @@ import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;
import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.index.query.QueryBuilder;
import org.sonar.core.profiling.Profiling;
import org.sonar.core.profiling.StopWatch;
+import java.io.IOException;
+
public class ProxyDeleteByQueryRequestBuilder extends DeleteByQueryRequestBuilder {
private final Profiling profiling;
+ private QueryBuilder internalBuilder;
+
public ProxyDeleteByQueryRequestBuilder(Client client, Profiling profiling) {
super(client);
this.profiling = profiling;
@@ -68,12 +76,31 @@ public class ProxyDeleteByQueryRequestBuilder extends DeleteByQueryRequestBuilde
}
@Override
+ public DeleteByQueryRequestBuilder setQuery(QueryBuilder queryBuilder) {
+ this.internalBuilder = queryBuilder;
+ return super.setQuery(queryBuilder);
+ }
+
+ @Override
public String toString() {
StringBuilder message = new StringBuilder();
- message.append("ES delete by query request");
+ message.append(String.format("ES delete by query request '%s'", xContentToString(internalBuilder)));
if (request.indices().length > 0) {
message.append(String.format(" on indices '%s'", StringUtils.join(request.indices(), ",")));
}
return message.toString();
}
+
+ private String xContentToString(ToXContent toXContent) {
+ if (internalBuilder == null) {
+ return "";
+ }
+ try {
+ XContentBuilder builder = XContentFactory.jsonBuilder();
+ toXContent.toXContent(builder, ToXContent.EMPTY_PARAMS);
+ return builder.string();
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to convert request to string", e);
+ }
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java
index aa17fafdffc..aeda87979ea 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java
@@ -20,27 +20,37 @@
package org.sonar.server.rule;
+import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import org.sonar.api.ServerComponent;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
+import org.sonar.api.server.rule.RuleParamType;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.rule.index.RuleDoc;
+import org.sonar.server.util.TypeValidations;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
public class RuleCreator implements ServerComponent {
private final DbClient dbClient;
- public RuleCreator(DbClient dbClient) {
+ private final TypeValidations typeValidations;
+
+ public RuleCreator(DbClient dbClient, TypeValidations typeValidations) {
this.dbClient = dbClient;
+ this.typeValidations = typeValidations;
}
public RuleKey create(NewRule newRule) {
@@ -71,6 +81,7 @@ public class RuleCreator implements ServerComponent {
throw new IllegalArgumentException("This rule is not a template rule: " + templateKey.toString());
}
validateCustomRule(newRule);
+ validateCustomRuleParams(newRule, dbSession, templateKey);
RuleKey customRuleKey = RuleKey.of(templateRule.getRepositoryKey(), newRule.ruleKey());
@@ -116,6 +127,25 @@ public class RuleCreator implements ServerComponent {
}
}
+ protected void validateCustomRuleParams(NewRule newRule, DbSession dbSession, RuleKey templateKey) {
+ for (RuleParamDto ruleParam : dbClient.ruleDao().findRuleParamsByRuleKey(dbSession, templateKey)) {
+ validateParam(ruleParam, newRule.parameter(ruleParam.getName()));
+ }
+ }
+
+ @CheckForNull
+ private void validateParam(RuleParamDto ruleParam, @Nullable String value) {
+ if (value != null) {
+ RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType());
+ if (ruleParamType.multiple()) {
+ List<String> values = newArrayList(Splitter.on(",").split(value));
+ typeValidations.validate(values, ruleParamType.type(), ruleParamType.values());
+ } else {
+ typeValidations.validate(value, ruleParamType.type(), ruleParamType.values());
+ }
+ }
+ }
+
private static void validateManualRule(NewRule newRule) {
validateRuleKey(newRule.ruleKey());
validateName(newRule);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
index 06d92231bd4..68fced94686 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
@@ -221,12 +221,8 @@ public class UserSession {
* Ensures that user implies the specified project permission on a component. If not a {@link org.sonar.server.exceptions.ForbiddenException} is thrown.
*/
public UserSession checkComponentPermission(String projectPermission, String componentKey) {
- return checkComponentPermission(projectPermission, componentKey, INSUFFICIENT_PRIVILEGES_MESSAGE);
- }
-
- public UserSession checkComponentPermission(String projectPermission, String componentKey, @Nullable String errorMessage) {
if (!hasComponentPermission(projectPermission, componentKey)) {
- throw new ForbiddenException(errorMessage);
+ throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
}
return this;
}
diff --git a/server/sonar-server/src/main/resources/com/sonar/sqale/technical-debt-model.xml b/server/sonar-server/src/main/resources/com/sonar/sqale/technical-debt-model.xml
index d2276e82e5d..4e3277a44b6 100644
--- a/server/sonar-server/src/main/resources/com/sonar/sqale/technical-debt-model.xml
+++ b/server/sonar-server/src/main/resources/com/sonar/sqale/technical-debt-model.xml
@@ -1,24 +1,3 @@
-<!--
-
- SonarQube, open source software quality management tool.
- Copyright (C) 2008-2014 SonarSource
- mailto:contact AT sonarsource DOT com
-
- SonarQube is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
-
- SonarQube is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
--->
<sqale>
<chc>
<key>REUSABILITY</key>
@@ -28,6 +7,10 @@
<name>Modularity</name>
</chc>
<chc>
+ <key>REUSABILITY_COMPLIANCE</key>
+ <name>Reusability Compliance</name>
+ </chc>
+ <chc>
<key>TRANSPORTABILITY</key>
<name>Transportability</name>
</chc>
@@ -52,6 +35,10 @@
<name>OS</name>
</chc>
<chc>
+ <key>PORTABILITY_COMPLIANCE</key>
+ <name>Portability Compliance</name>
+ </chc>
+ <chc>
<key>SOFTWARE_RELATED_PORTABILITY</key>
<name>Software</name>
</chc>
@@ -64,6 +51,10 @@
<key>MAINTAINABILITY</key>
<name>Maintainability</name>
<chc>
+ <key>MAINTAINABILITY_COMPLIANCE</key>
+ <name>Maintainability Compliance</name>
+ </chc>
+ <chc>
<key>READABILITY</key>
<name>Readability</name>
</chc>
@@ -88,11 +79,31 @@
<name>Input validation and representation</name>
</chc>
<chc>
+ <key>SECURITY_COMPLIANCE</key>
+ <name>Security Compliance</name>
+ </chc>
+ <chc>
<key>SECURITY_FEATURES</key>
<name>Security features</name>
</chc>
</chc>
<chc>
+ <key>USABILITY</key>
+ <name>Usability</name>
+ <chc>
+ <key>USABILITY_ACCESSIBILITY</key>
+ <name>Accessibility</name>
+ </chc>
+ <chc>
+ <key>USABILITY_EASE_OF_USE</key>
+ <name>Ease of Use</name>
+ </chc>
+ <chc>
+ <key>USABILITY_COMPLIANCE</key>
+ <name>Usability Compliance</name>
+ </chc>
+ </chc>
+ <chc>
<key>EFFICIENCY</key>
<name>Efficiency</name>
<chc>
@@ -100,6 +111,10 @@
<name>Processor use</name>
</chc>
<chc>
+ <key>EFFICIENCY_COMPLIANCE</key>
+ <name>Efficiency Compliance</name>
+ </chc>
+ <chc>
<key>MEMORY_EFFICIENCY</key>
<name>Memory use</name>
</chc>
@@ -116,6 +131,10 @@
<name>Architecture</name>
</chc>
<chc>
+ <key>CHANGEABILITY_COMPLIANCE</key>
+ <name>Changeability Compliance</name>
+ </chc>
+ <chc>
<key>DATA_CHANGEABILITY</key>
<name>Data</name>
</chc>
@@ -152,6 +171,10 @@
<name>Logic</name>
</chc>
<chc>
+ <key>RELIABILITY_COMPLIANCE</key>
+ <name>Reliability Compliance</name>
+ </chc>
+ <chc>
<key>RESOURCE_RELIABILITY</key>
<name>Resource</name>
</chc>
@@ -172,6 +195,10 @@
<name>Integration level</name>
</chc>
<chc>
+ <key>TESTABILITY_COMPLIANCE</key>
+ <name>Testability Compliance</name>
+ </chc>
+ <chc>
<key>UNIT_TESTABILITY</key>
<name>Unit level</name>
</chc>
diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java
index 1a1f7a62003..ea73b0dea18 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java
@@ -86,7 +86,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_project_settings() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -110,7 +110,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void not_returned_secured_settings_with_only_preview_permission() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION).addProjectUuidPermissions(UserRole.USER, project.uuid());
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -133,7 +133,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_project_with_module_settings() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -169,7 +169,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_project_with_module_settings_inherited_from_project() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -200,7 +200,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_project_with_module_with_sub_module() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -248,7 +248,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_project_with_two_modules() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -293,7 +293,7 @@ public class ProjectRepositoryLoaderMediumTest {
public void return_provisioned_project_settings() throws Exception {
// No snapshot attached on the project -> provisioned project
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -323,7 +323,7 @@ public class ProjectRepositoryLoaderMediumTest {
// No module properties
ComponentDto subModule = ComponentTesting.newModuleDto(module);
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), subModule.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, subModule);
// Sub module properties
@@ -359,7 +359,7 @@ public class ProjectRepositoryLoaderMediumTest {
tester.get(DbClient.class).propertiesDao().setProperty(new PropertyDto().setKey("sonar.jira.login.secured").setValue("john").setResourceId(module.getId()), dbSession);
ComponentDto subModule = ComponentTesting.newModuleDto(module);
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), subModule.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, subModule);
// Sub module properties
@@ -393,7 +393,7 @@ public class ProjectRepositoryLoaderMediumTest {
// No module property
ComponentDto subModule = ComponentTesting.newModuleDto(module);
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), subModule.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, subModule);
// No sub module property
@@ -426,7 +426,7 @@ public class ProjectRepositoryLoaderMediumTest {
tester.get(DbClient.class).propertiesDao().setProperty(new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR-SERVER").setResourceId(module.getId()), dbSession);
ComponentDto subModule = ComponentTesting.newModuleDto(module);
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), subModule.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, subModule);
// No sub module property
@@ -447,7 +447,7 @@ public class ProjectRepositoryLoaderMediumTest {
Date ruleUpdatedAt = DateUtils.parseDateTime("2014-01-14T13:00:00+0100");
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt(
@@ -471,7 +471,7 @@ public class ProjectRepositoryLoaderMediumTest {
Date ruleUpdatedAt = DateUtils.parseDateTime("2014-01-14T13:00:00+0100");
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt(
@@ -495,7 +495,7 @@ public class ProjectRepositoryLoaderMediumTest {
Date ruleUpdatedAt = DateUtils.parseDateTime("2014-01-14T13:00:00+0100");
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt(
@@ -541,7 +541,7 @@ public class ProjectRepositoryLoaderMediumTest {
// No snapshot attached on the project -> provisioned project
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt(
@@ -563,7 +563,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void fail_when_no_quality_profile_for_a_language() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto().setKey("org.codehaus.sonar:sonar");
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
dbSession.commit();
@@ -580,7 +580,7 @@ public class ProjectRepositoryLoaderMediumTest {
Date ruleUpdatedAt = DateUtils.parseDateTime("2014-01-14T13:00:00+0100");
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt(
@@ -614,11 +614,34 @@ public class ProjectRepositoryLoaderMediumTest {
}
@Test
+ public void return_more_than_10_active_rules() throws Exception {
+ ComponentDto project = ComponentTesting.newProjectDto();
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+ tester.get(DbClient.class).componentDao().insert(dbSession, project);
+
+ QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd")
+ .setRulesUpdatedAt(DateUtils.formatDateTime(DateUtils.parseDateTime("2014-01-14T13:00:00+0100")));
+ tester.get(DbClient.class).qualityProfileDao().insert(dbSession, profileDto);
+ tester.get(DbClient.class).propertiesDao().setProperty(new PropertyDto().setKey("sonar.profile.xoo").setValue("SonarQube way"), dbSession);
+
+ for (int i = 0; i<20; i++) {
+ RuleKey ruleKey = RuleKey.of("squid", "Rule" + i);
+ tester.get(DbClient.class).ruleDao().insert(dbSession, RuleTesting.newDto(ruleKey).setName("Rule" + i).setLanguage(ServerTester.Xoo.KEY));
+ tester.get(RuleActivator.class).activate(dbSession, new RuleActivation(ruleKey).setSeverity(Severity.MINOR), profileDto.getKey());
+ }
+
+ dbSession.commit();
+
+ ProjectRepositories ref = loader.load(ProjectRepositoryQuery.create().setModuleKey(project.key()));
+ assertThat(ref.activeRules()).hasSize(20);
+ }
+
+ @Test
public void return_custom_rule() throws Exception {
Date ruleUpdatedAt = DateUtils.parseDateTime("2014-01-14T13:00:00+0100");
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentUuidPermission(UserRole.USER, project.uuid(), project.uuid());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt(
@@ -649,7 +672,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_manual_rules() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -704,7 +727,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_file_data_from_single_project() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -723,7 +746,7 @@ public class ProjectRepositoryLoaderMediumTest {
@Test
public void return_file_data_from_multi_modules() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), project.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, project);
addDefaultProfile();
@@ -759,7 +782,7 @@ public class ProjectRepositoryLoaderMediumTest {
tester.get(FileSourceDao.class).insert(newFileSourceDto(projectFile).setSrcHash("123456"));
ComponentDto module = ComponentTesting.newModuleDto(project);
- MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION).addComponentPermission(UserRole.USER, project.getKey(), module.getKey());
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
tester.get(DbClient.class).componentDao().insert(dbSession, module);
// File on module
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java
index 09a5c0a11c4..cb156004ef6 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/BaseDataChangeTest.java
@@ -134,7 +134,7 @@ public class BaseDataChangeTest extends AbstractDaoTestCase {
context.prepareSelect("select id from persons order by id desc").scroll(new Select.RowHandler() {
@Override
public void handle(Select.Row row) throws SQLException {
- ids.add(row.getLong(1));
+ ids.add(row.getNullableLong(1));
}
});
}
@@ -252,7 +252,7 @@ public class BaseDataChangeTest extends AbstractDaoTestCase {
context.prepareSelect("select id from persons").scroll(new Select.RowHandler() {
@Override
public void handle(Select.Row row) throws SQLException {
- long id = row.getLong(1);
+ long id = row.getNullableLong(1);
upsert.setString(1, "login" + id).setInt(2, 10 + (int) id).setLong(3, id);
upsert.execute();
}
@@ -277,7 +277,7 @@ public class BaseDataChangeTest extends AbstractDaoTestCase {
massUpdate.execute(new MassUpdate.Handler() {
@Override
public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
- long id = row.getLong(1);
+ long id = row.getNullableLong(1);
update
.setString(1, "login" + id)
.setInt(2, 10 + (int) id)
@@ -339,17 +339,52 @@ public class BaseDataChangeTest extends AbstractDaoTestCase {
}
}
+ @Test
+ public void read_not_null_fields() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final List<Object[]> persons = new ArrayList<Object[]>();
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ persons.addAll(context
+ .prepareSelect("select id,login,age,enabled,updated_at,coeff from persons where id=2")
+ .list(new Select.RowReader<Object[]>() {
+ @Override
+ public Object[] read(Select.Row row) throws SQLException {
+ return new Object[] {
+ // id, login, age, enabled
+ row.getLong(1),
+ row.getString(2),
+ row.getInt(3),
+ row.getBoolean(4),
+ row.getDate(5),
+ row.getDouble(6),
+ };
+ }
+ }));
+ }
+ }.execute();
+ assertThat(persons).hasSize(1);
+ assertThat(persons.get(0)[0]).isEqualTo(2L);
+ assertThat(persons.get(0)[1]).isEqualTo("emmerik");
+ assertThat(persons.get(0)[2]).isEqualTo(14);
+ assertThat(persons.get(0)[3]).isEqualTo(true);
+ assertThat(persons.get(0)[4]).isNotNull();
+ assertThat(persons.get(0)[5]).isEqualTo(5.2);
+ }
+
static class UserReader implements Select.RowReader<Object[]> {
@Override
public Object[] read(Select.Row row) throws SQLException {
- return new Object[]{
+ return new Object[] {
// id, login, age, enabled
- row.getLong(1),
- row.getString(2),
- row.getInt(3),
- row.getBoolean(4),
- row.getDate(5),
- row.getDouble(6),
+ row.getNullableLong(1),
+ row.getNullableString(2),
+ row.getNullableInt(3),
+ row.getNullableBoolean(4),
+ row.getNullableDate(5),
+ row.getNullableDouble(6),
};
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest.java
new file mode 100644
index 00000000000..f14fbebf3c2
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest.java
@@ -0,0 +1,131 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.db.migrations.v51;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.db.migrations.DatabaseMigration;
+
+import static junit.framework.TestCase.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest {
+
+ @ClassRule
+ public static DbTester db = new DbTester().schema(AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest.class, "schema.sql");
+
+ DatabaseMigration migration;
+
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() throws Exception {
+ db.executeUpdateSql("truncate table characteristics");
+
+ when(system.now()).thenReturn(DateUtils.parseDate("2015-02-15").getTime());
+
+ migration = new AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration(db.database(), system);
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "migrate-result.xml", "characteristics");
+ }
+
+ @Test
+ public void do_nothing_when_already_migrated() throws Exception {
+ db.prepareDbUnit(getClass(), "do_nothing_when_already_migrated.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "do_nothing_when_already_migrated.xml", "characteristics");
+ }
+
+ @Test
+ public void do_nothing_when_no_characteristics() throws Exception {
+ db.prepareDbUnit(getClass(), "empty.xml");
+ migration.execute();
+ assertThat(db.countRowsOfTable("characteristics")).isEqualTo(0);
+ }
+
+ @Test
+ public void insert_usability_at_the_top_if_security_does_exists() throws Exception {
+ db.prepareDbUnit(getClass(), "insert_usability_at_the_top_if_security_does_exists.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "insert_usability_at_the_top_if_security_does_exists-result.xml", "characteristics");
+ }
+
+ @Test
+ public void update_usability_order_if_already_exists() throws Exception {
+ db.prepareDbUnit(getClass(), "update_usability_if_already_exists.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "update_usability_if_already_exists-result.xml", "characteristics");
+ }
+
+ @Test
+ public void fail_if_usability_exists_as_sub_characteristic() throws Exception {
+ db.prepareDbUnit(getClass(), "fail_if_usability_exists_as_sub_characteristic.xml");
+
+ try {
+ migration.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("'Usability' must be a characteristic");
+ }
+ }
+
+ @Test
+ public void fail_if_compliance_already_exists_as_characteristic() throws Exception {
+ db.prepareDbUnit(getClass(), "fail_if_compliance_already_exists_as_characteristic.xml");
+
+ try {
+ migration.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("'Compliance' must be a sub-characteristic");
+ }
+ }
+
+ @Test
+ public void fail_if_compliance_already_exists_under_wrong_characteristic() throws Exception {
+ db.prepareDbUnit(getClass(), "fail_if_compliance_already_exists_under_wrong_characteristic.xml");
+
+ try {
+ migration.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("'Reusability Compliance' must be defined under 'Reusability'");
+ }
+ }
+
+ @Test
+ public void not_fail_if_some_deprecated_requirements_still_exists_in_db() throws Exception {
+ db.prepareDbUnit(getClass(), "not_fail_if_some_deprecated_requirements_still_exists_in_db.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "not_fail_if_some_deprecated_requirements_still_exists_in_db.xml", "characteristics");
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest.java
new file mode 100644
index 00000000000..4076ba2e382
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest.java
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.db.migrations.v51;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.db.migrations.DatabaseMigration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.DateUtils.parseDate;
+
+public class FeedEventsLongDatesTest {
+ @ClassRule
+ public static DbTester db = new DbTester().schema(FeedEventsLongDatesTest.class, "schema.sql");
+
+ @Before
+ public void before() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+ }
+
+ @Test
+ public void execute() throws Exception {
+ DatabaseMigration migration = newMigration(System2.INSTANCE);
+
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from events where " +
+ "created_at_ms is not null " +
+ "and event_date_ms is not null");
+ assertThat(count).isEqualTo(3);
+ }
+
+ @Test
+ public void take_now_if_date_in_the_future() throws Exception {
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1234L);
+
+ DatabaseMigration migration = newMigration(system);
+
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from events where " +
+ "created_at_ms = 1234");
+ assertThat(count).isEqualTo(2);
+ }
+
+ @Test
+ public void take_date_if_in_the_past() throws Exception {
+ DatabaseMigration migration = newMigration(System2.INSTANCE);
+
+ migration.execute();
+
+ long time = parseDate("2014-09-25").getTime();
+ int count = db
+ .countSql("select count(*) from events where " +
+ "created_at_ms=" + time);
+ assertThat(count).isEqualTo(1);
+ }
+
+ private DatabaseMigration newMigration(System2 system) {
+ return new FeedEventsLongDates(db.database(), system);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java
index c3b2a47c45d..d91efe79024 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java
@@ -70,7 +70,7 @@ public class FeedManualMeasuresLongDatesTest {
}
@Test
- public void take_snapshot_date_if_in_the_past() throws Exception {
+ public void take_manual_measure_date_if_in_the_past() throws Exception {
DatabaseMigration migration = newMigration(System2.INSTANCE);
migration.execute();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/debt/DebtMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/debt/DebtMediumTest.java
index c0b0c174862..d01b7299644 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/debt/DebtMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/debt/DebtMediumTest.java
@@ -44,14 +44,14 @@ public class DebtMediumTest {
}
@Test
- public void find_characteristics() throws Exception {
+ public void find_default_characteristics() throws Exception {
DebtModelService debtModelService = serverTester.get(DebtModelService.class);
// Only root characteristics
- assertThat(debtModelService.characteristics()).hasSize(8);
+ assertThat(debtModelService.characteristics()).hasSize(9);
// Characteristics and sub-characteristics
- assertThat(debtModelService.allCharacteristics()).hasSize(39);
+ assertThat(debtModelService.allCharacteristics()).hasSize(51);
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyBulkRequestBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyBulkRequestBuilderTest.java
index f96e451dd0b..e79cc839d28 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyBulkRequestBuilderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyBulkRequestBuilderTest.java
@@ -66,6 +66,9 @@ public class ProxyBulkRequestBuilderTest {
req.add(new IndexRequest(FakeIndexDefinition.INDEX, FakeIndexDefinition.TYPE, "key3")
.source(FakeIndexDefinition.newDoc(3)));
+ assertThat(req.toString()).isEqualTo(
+ "Bulk[1 delete request(s) on index fakes and type fake, 1 update request(s) on index fakes and type fake, 1 index request(s) on index fakes and type fake]");
+
BulkResponse response = req.get();
assertThat(response.getItems()).hasSize(3);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilderTest.java
index b43698ea882..e602a69ab21 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/es/request/ProxyDeleteByQueryRequestBuilderTest.java
@@ -49,8 +49,9 @@ public class ProxyDeleteByQueryRequestBuilderTest {
@Test
public void to_string() {
- assertThat(esTester.client().prepareDeleteByQuery(FakeIndexDefinition.INDEX).toString()).isEqualTo("ES delete by query request on indices 'fakes'");
- assertThat(esTester.client().prepareDeleteByQuery().toString()).isEqualTo("ES delete by query request");
+ assertThat(esTester.client().prepareDeleteByQuery(FakeIndexDefinition.INDEX).setQuery(QueryBuilders.matchAllQuery()).toString()).isEqualTo(
+ "ES delete by query request '{\"match_all\":{}}' on indices 'fakes'");
+ assertThat(esTester.client().prepareDeleteByQuery().toString()).isEqualTo("ES delete by query request ''");
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorMediumTest.java
index 8b013c8e299..442236806a8 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorMediumTest.java
@@ -20,10 +20,9 @@
package org.sonar.server.rule;
-import org.sonar.server.search.BaseIndex;
-
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
+import org.assertj.core.api.Fail;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
@@ -37,8 +36,10 @@ import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleDto.Format;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.rule.index.RuleIndex;
+import org.sonar.server.search.BaseIndex;
import org.sonar.server.tester.ServerTester;
import java.util.List;
@@ -142,7 +143,7 @@ public class RuleCreatorMediumTest {
@Test
public void create_custom_rule_with_no_parameter_value() throws Exception {
// insert template rule
- RuleDto templateRule = createTemplateRule();
+ RuleDto templateRule = createTemplateRuleWithIntArrayParam();
NewRule newRule = NewRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
.setName("My custom")
@@ -157,13 +158,60 @@ public class RuleCreatorMediumTest {
assertThat(params).hasSize(1);
RuleParamDto param = params.get(0);
- assertThat(param.getName()).isEqualTo("regex");
- assertThat(param.getDescription()).isEqualTo("Reg ex");
- assertThat(param.getType()).isEqualTo("STRING");
+ assertThat(param.getName()).isEqualTo("myIntegers");
+ assertThat(param.getDescription()).isEqualTo("My Integers");
+ assertThat(param.getType()).isEqualTo("INTEGER,multiple=true,values=1;2;3");
assertThat(param.getDefaultValue()).isNull();
}
@Test
+ public void create_custom_rule_with_multiple_parameter_values() throws Exception {
+ // insert template rule
+ RuleDto templateRule = createTemplateRuleWithIntArrayParam();
+
+ NewRule newRule = NewRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
+ .setName("My custom")
+ .setHtmlDescription("Some description")
+ .setSeverity(Severity.MAJOR)
+ .setStatus(RuleStatus.READY)
+ .setParameters(ImmutableMap.of("myIntegers", "1,3"));
+
+ RuleKey customRuleKey = creator.create(newRule);
+ dbSession.clearCache();
+
+ List<RuleParamDto> params = db.ruleDao().findRuleParamsByRuleKey(dbSession, customRuleKey);
+ assertThat(params).hasSize(1);
+
+ RuleParamDto param = params.get(0);
+ assertThat(param.getName()).isEqualTo("myIntegers");
+ assertThat(param.getDescription()).isEqualTo("My Integers");
+ assertThat(param.getType()).isEqualTo("INTEGER,multiple=true,values=1;2;3");
+ assertThat(param.getDefaultValue()).isEqualTo("1,3");
+ }
+
+ @Test
+ public void create_custom_rule_with_invalid_parameter() throws Exception {
+ // insert template rule
+ RuleDto templateRule = createTemplateRuleWithIntArrayParam();
+
+ // Create custom rule
+ NewRule newRule = NewRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
+ .setName("My custom")
+ .setMarkdownDescription("Some description")
+ .setSeverity(Severity.MAJOR)
+ .setStatus(RuleStatus.READY)
+ .setParameters(ImmutableMap.of("myIntegers", "1,polop,2"));
+ try {
+ creator.create(newRule);
+ Fail.failBecauseExceptionWasNotThrown(BadRequestException.class);
+ } catch (BadRequestException iae) {
+ assertThat(iae).hasMessage("errors.type.notInteger");
+ }
+
+ dbSession.clearCache();
+ }
+
+ @Test
public void reactivate_custom_rule_if_already_exists_in_removed_status() throws Exception {
String key = "CUSTOM_RULE";
@@ -614,4 +662,22 @@ public class RuleCreatorMediumTest {
return templateRule;
}
+ private RuleDto createTemplateRuleWithIntArrayParam() {
+ RuleDto templateRule = dao.insert(dbSession,
+ RuleTesting.newDto(RuleKey.of("java", "S002"))
+ .setIsTemplate(true)
+ .setLanguage("java")
+ .setConfigKey("S002")
+ .setDefaultSubCharacteristicId(1)
+ .setDefaultRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
+ .setDefaultRemediationCoefficient("1h")
+ .setDefaultRemediationOffset("5min")
+ .setEffortToFixDescription("desc")
+ );
+ RuleParamDto ruleParamDto = RuleParamDto.createFor(templateRule)
+ .setName("myIntegers").setType("INTEGER,multiple=true,values=1;2;3").setDescription("My Integers").setDefaultValue("1");
+ dao.addRuleParam(dbSession, templateRule, ruleParamDto);
+ dbSession.commit();
+ return templateRule;
+ }
}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/do_nothing_when_already_migrated.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/do_nothing_when_already_migrated.xml
new file mode 100644
index 00000000000..b8ff37c389e
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/do_nothing_when_already_migrated.xml
@@ -0,0 +1,53 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="2" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="3" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="4" kee="PORTABILITY_COMPLIANCE" name="Portability Compliance" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="5" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" rule_id="[null]" characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="6" kee="MAINTAINABILITY_COMPLIANCE" name="Maintainability Compliance" parent_id="5" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="7" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="8" kee="SECURITY_COMPLIANCE" name="Security Compliance" parent_id="7" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+
+ <characteristics id="9" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="5" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="10" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="9" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="11" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="9" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="12" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="9" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="13" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]" characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="14" kee="EFFICIENCY_COMPLIANCE" name="Efficiency Compliance" parent_id="13" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="15" kee="CHANGEABILITY" name="Changeability" parent_id="[null]" rule_id="[null]" characteristic_order="7" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="16" kee="CHANGEABILITY_COMPLIANCE" name="Changeability Compliance" parent_id="15" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="17" kee="RELIABILITY" name="Reliability" parent_id="[null]" rule_id="[null]" characteristic_order="8" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="18" kee="RELIABILITY_COMPLIANCE" name="Reliability Compliance" parent_id="17" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="19" kee="TESTABILITY" name="Testability" parent_id="[null]" rule_id="[null]" characteristic_order="9" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="20" kee="TESTABILITY_COMPLIANCE" name="Testability Compliance" parent_id="19" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/empty.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/empty.xml
new file mode 100644
index 00000000000..871dedcb5e9
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/empty.xml
@@ -0,0 +1,3 @@
+<dataset>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_as_characteristic.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_as_characteristic.xml
new file mode 100644
index 00000000000..1f2f39bdbef
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_as_characteristic.xml
@@ -0,0 +1,9 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="REUSABILITY_COMPLIANCE" name="Compliance" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml
new file mode 100644
index 00000000000..3dbfebd4153
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="3" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="2" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20"
+ updated_at="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_usability_exists_as_sub_characteristic.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_usability_exists_as_sub_characteristic.xml
new file mode 100644
index 00000000000..70016cc2d9d
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/fail_if_usability_exists_as_sub_characteristic.xml
@@ -0,0 +1,9 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="USABILITY" name="Usability" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists-result.xml
new file mode 100644
index 00000000000..4f596203e6d
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists-result.xml
@@ -0,0 +1,30 @@
+<dataset>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]" characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- New characteristic 'Usability' is on the top (order 1) -->
+ <characteristics id="3" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="4" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="5" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="6" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+
+ <!-- New sub characteristic 'Compliance' under Reusability -->
+ <characteristics id="7" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Portability -->
+ <characteristics id="8" kee="PORTABILITY_COMPLIANCE" name="Portability Compliance" parent_id="2" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists.xml
new file mode 100644
index 00000000000..7ab9a9726ac
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/insert_usability_at_the_top_if_security_does_exists.xml
@@ -0,0 +1,9 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate-result.xml
new file mode 100644
index 00000000000..91f1c6ab778
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate-result.xml
@@ -0,0 +1,74 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="3" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" rule_id="[null]" characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="4" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="5" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]" characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="6" kee="CHANGEABILITY" name="Changeability" parent_id="[null]" rule_id="[null]" characteristic_order="7" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="7" kee="RELIABILITY" name="Reliability" parent_id="[null]" rule_id="[null]" characteristic_order="8" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="8" kee="TESTABILITY" name="Testability" parent_id="[null]" rule_id="[null]" characteristic_order="9" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- New characteristic 'Usability' is after Security -->
+ <characteristics id="9" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="5" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <!-- New sub characteristics under Usability -->
+ <characteristics id="10" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="9" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="11" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="9" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="12" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="9" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Reusability -->
+ <characteristics id="13" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Portability -->
+ <characteristics id="14" kee="PORTABILITY_COMPLIANCE" name="Portability Compliance" parent_id="2" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Maintainability -->
+ <characteristics id="15" kee="MAINTAINABILITY_COMPLIANCE" name="Maintainability Compliance" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Security -->
+ <characteristics id="16" kee="SECURITY_COMPLIANCE" name="Security Compliance" parent_id="4" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Efficiency -->
+ <characteristics id="17" kee="EFFICIENCY_COMPLIANCE" name="Efficiency Compliance" parent_id="5" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Changeability -->
+ <characteristics id="18" kee="CHANGEABILITY_COMPLIANCE" name="Changeability Compliance" parent_id="6" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Reliability -->
+ <characteristics id="19" kee="RELIABILITY_COMPLIANCE" name="Reliability Compliance" parent_id="7" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Testability -->
+ <characteristics id="20" kee="TESTABILITY_COMPLIANCE" name="Testability Compliance" parent_id="8" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate.xml
new file mode 100644
index 00000000000..2d92c7cc413
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/migrate.xml
@@ -0,0 +1,27 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]" characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="3" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" rule_id="[null]" characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="4" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="5" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]" characteristic_order="5" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="6" kee="CHANGEABILITY" name="Changeability" parent_id="[null]" rule_id="[null]" characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="7" kee="RELIABILITY" name="Reliability" parent_id="[null]" rule_id="[null]" characteristic_order="7" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="8" kee="TESTABILITY" name="Testability" parent_id="[null]" rule_id="[null]" characteristic_order="8" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml
new file mode 100644
index 00000000000..6953f5230bb
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml
@@ -0,0 +1,14 @@
+<dataset>
+
+ <characteristics id="1" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="2" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="3" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="4" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="5" kee="[null]" name="[null]" parent_id="3" rule_id="3" characteristic_order="[null]" enabled="[true]" created_at="2013-11-20" updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/schema.sql
new file mode 100644
index 00000000000..98c025def6b
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/schema.sql
@@ -0,0 +1,11 @@
+CREATE TABLE "CHARACTERISTICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(100),
+ "NAME" VARCHAR(100),
+ "PARENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "CHARACTERISTIC_ORDER" INTEGER,
+ "ENABLED" BOOLEAN,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists-result.xml
new file mode 100644
index 00000000000..1b12df0872e
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists-result.xml
@@ -0,0 +1,30 @@
+<dataset>
+
+ <characteristics id="1" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="2" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]" characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Usability is moved after Security -->
+ <characteristics id="3" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="5" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- New sub characteristics under Usability -->
+ <characteristics id="4" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="5" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="6" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="3" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Security -->
+ <characteristics id="7" kee="SECURITY_COMPLIANCE" name="Security Compliance" parent_id="1" rule_id="[null]" characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Efficiency -->
+ <characteristics id="8" kee="EFFICIENCY_COMPLIANCE" name="Efficiency Compliance" parent_id="2" rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists.xml
new file mode 100644
index 00000000000..33d901de062
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigrationTest/update_usability_if_already_exists.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <characteristics id="1" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]" characteristic_order="5" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <!-- Usability should be move after Security -->
+ <characteristics id="3" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/before.xml
new file mode 100644
index 00000000000..52ad14d35b2
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/before.xml
@@ -0,0 +1,28 @@
+<dataset>
+ <!-- new migration -->
+ <events
+ id="1"
+ created_at="2014-09-25"
+ created_at_ms="[null]"
+ event_date="2014-09-25"
+ event_date_ms="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the ones that are already fed with new dates -->
+ <events
+ id="2"
+ created_at="2014-09-25"
+ created_at_ms="1500000000"
+ event_date="2014-09-25"
+ event_date_ms="1500000000"
+ />
+
+ <!-- NULL dates -->
+ <events
+ id="3"
+ created_at="[null]"
+ created_at_ms="[null]"
+ event_date="[null]"
+ event_date_ms="[null]"
+ />
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/schema.sql
new file mode 100644
index 00000000000..71ac42d40ef
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedEventsLongDatesTest/schema.sql
@@ -0,0 +1,7 @@
+CREATE TABLE "EVENTS" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "EVENT_DATE" TIMESTAMP,
+ "EVENT_DATE_MS" BIGINT
+);
diff --git a/server/sonar-web/src/main/coffee/issue/issue-view.coffee b/server/sonar-web/src/main/coffee/issue/issue-view.coffee
index 622ff88cd98..e0028cf404b 100644
--- a/server/sonar-web/src/main/coffee/issue/issue-view.coffee
+++ b/server/sonar-web/src/main/coffee/issue/issue-view.coffee
@@ -201,7 +201,7 @@ define [
assignToMe: ->
- view = new AssignFormView model: @model
+ view = new AssignFormView model: @model, triggerEl: $('body')
view.submit window.SS.user, window.SS.userName
view.close()
diff --git a/server/sonar-web/src/main/coffee/issues/facets/creation-date-facet.coffee b/server/sonar-web/src/main/coffee/issues/facets/creation-date-facet.coffee
index 42acf9397f9..f9c20fbb737 100644
--- a/server/sonar-web/src/main/coffee/issues/facets/creation-date-facet.coffee
+++ b/server/sonar-web/src/main/coffee/issues/facets/creation-date-facet.coffee
@@ -18,6 +18,8 @@ define [
'click .js-select-period-start': 'selectPeriodStart'
'click .js-select-period-end': 'selectPeriodEnd'
+ 'click .sonar-d3 rect': 'selectBar'
+
'click .js-all': 'onAllClick'
'click .js-last-week': 'onLastWeekClick'
'click .js-last-month': 'onLastMonthClick'
@@ -70,6 +72,12 @@ define [
@options.app.state.updateFilter createdAfter: null, createdBefore: null, createdAt: null
+ selectBar: (e) ->
+ periodStart = $(e.currentTarget).data 'period-start'
+ periodEnd = $(e.currentTarget).data 'period-end'
+ @options.app.state.updateFilter createdAfter: periodStart, createdBefore: periodEnd, createdAt: null
+
+
onAllClick: ->
@disable()
diff --git a/server/sonar-web/src/main/coffee/issues/models/state.coffee b/server/sonar-web/src/main/coffee/issues/models/state.coffee
index b16dea09c72..1cbaafeb8b6 100644
--- a/server/sonar-web/src/main/coffee/issues/models/state.coffee
+++ b/server/sonar-web/src/main/coffee/issues/models/state.coffee
@@ -7,10 +7,10 @@ define [
page: 1
maxResultsReached: false
query: {}
- facets: ['severities', 'resolutions', 'createdAt', 'rules', 'tags', 'projectUuids']
+ facets: ['severities', 'resolutions']
isContext: false
- allFacets: ['issues', 'severities', 'resolutions', 'createdAt', 'rules', 'tags', 'statuses', 'projectUuids',
+ allFacets: ['issues', 'severities', 'resolutions', 'statuses', 'createdAt', 'rules', 'tags', 'projectUuids',
'moduleUuids', 'directories', 'fileUuids', 'assignees', 'reporters', 'authors', 'languages',
'actionPlans'],
facetsFromServer: ['severities', 'statuses', 'resolutions', 'actionPlans', 'projectUuids', 'directories', 'rules',
diff --git a/server/sonar-web/src/main/coffee/quality-gate/app.coffee b/server/sonar-web/src/main/coffee/quality-gate/app.coffee
index 28cf9daf416..1e376e3b2b5 100644
--- a/server/sonar-web/src/main/coffee/quality-gate/app.coffee
+++ b/server/sonar-web/src/main/coffee/quality-gate/app.coffee
@@ -18,10 +18,6 @@ requirejs [
QualityGateLayout
) ->
- # Add html class to mark the page as navigator page
- jQuery('html').addClass('navigator-page quality-gates-page');
-
-
# Create a Quality Gate Application
App = new Marionette.Application
@@ -49,8 +45,8 @@ requirejs [
# Construct layout
App.addInitializer ->
@layout = new QualityGateLayout app: @
- jQuery('#content').append @layout.render().el
- @layout.onResize()
+ jQuery('#quality-gates').append @layout.render().el
+ jQuery('#footer').addClass 'search-navigator-footer'
# Construct actions bar
@@ -101,8 +97,5 @@ requirejs [
jQuery.when(qualityGatesXHR, appXHR, l10nXHR)
.done ->
- # Remove the initial spinner
- jQuery('#quality-gate-page-loader').remove()
-
# Start the application
App.start()
diff --git a/server/sonar-web/src/main/coffee/quality-gate/layout.coffee b/server/sonar-web/src/main/coffee/quality-gate/layout.coffee
index 9a982cf1e02..4e58fa172e8 100644
--- a/server/sonar-web/src/main/coffee/quality-gate/layout.coffee
+++ b/server/sonar-web/src/main/coffee/quality-gate/layout.coffee
@@ -2,45 +2,21 @@ define [
'templates/quality-gates'
], ->
+ $ = jQuery
+
class AppLayout extends Marionette.Layout
- className: 'navigator quality-gates-navigator'
template: Templates['quality-gates-layout']
regions:
- headerRegion: '.navigator-header'
- actionsRegion: '.navigator-actions'
- resultsRegion: '.navigator-results'
- detailsRegion: '.navigator-details'
-
-
- initialize: (options) ->
- @listenTo options.app.qualityGates, 'all', @updateLayout
- jQuery(window).on 'resize', => @onResize()
-
-
- updateLayout: ->
- empty = @options.app.qualityGates.length == 0
- @$(@headerRegion.el).toggle !empty
- @$(@detailsRegion.el).toggle !empty
-
-
- onResize: ->
- footerEl = jQuery('#footer')
- footerHeight = footerEl.outerHeight true
-
- resultsEl = jQuery('.navigator-results')
- resultsHeight = jQuery(window).height() - resultsEl.offset().top -
- parseInt(resultsEl.css('margin-bottom'), 10) - footerHeight
- resultsEl.height resultsHeight
-
- detailsEl = jQuery('.navigator-details')
- detailsWidth = jQuery(window).width() - detailsEl.offset().left -
- parseInt(detailsEl.css('margin-right'), 10)
- detailsHeight = jQuery(window).height() - detailsEl.offset().top -
- parseInt(detailsEl.css('margin-bottom'), 10) - footerHeight
- detailsEl.width(detailsWidth).height detailsHeight
+ headerRegion: '.search-navigator-workspace-header'
+ actionsRegion: '.search-navigator-filters'
+ resultsRegion: '.quality-gates-results'
+ detailsRegion: '.search-navigator-workspace-list'
onRender: ->
- @updateLayout()
+ $('.search-navigator').addClass 'sticky'
+ top = $('.search-navigator').offset().top
+ @$('.search-navigator-workspace-header').css top: top
+ @$('.search-navigator-side').css({ top: top }).isolatedScroll()
diff --git a/server/sonar-web/src/main/coffee/quality-gate/router.coffee b/server/sonar-web/src/main/coffee/quality-gate/router.coffee
index 9bb56410f9a..e185428a51b 100644
--- a/server/sonar-web/src/main/coffee/quality-gate/router.coffee
+++ b/server/sonar-web/src/main/coffee/quality-gate/router.coffee
@@ -34,7 +34,5 @@ define [
@app.layout.detailsRegion.show qualityGateDetailView
qualityGateDetailView.$el.hide()
- qualityGateDetailHeaderView.showSpinner()
qualityGate.fetch().done ->
qualityGateDetailView.$el.show()
- qualityGateDetailHeaderView.hideSpinner()
diff --git a/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee b/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee
index 3bcc07e9eaf..a90b5580f2a 100644
--- a/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee
+++ b/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-detail-header-view.coffee
@@ -49,26 +49,21 @@ define [
yesLabel: t 'delete'
noLabel: t 'cancel'
yesHandler: =>
- @showSpinner()
jQuery.ajax
type: 'POST'
url: "#{baseUrl}/api/qualitygates/destroy"
data: id: @model.id
- .always => @hideSpinner()
.done => @options.app.deleteQualityGate @model.id
always: => @ui.deleteButton.blur()
changeDefault: (set) ->
- @showSpinner()
data = if set then { id: @model.id } else {}
method = if set then 'set_as_default' else 'unset_default'
jQuery.ajax
type: 'POST'
url: "#{baseUrl}/api/qualitygates/#{method}"
data: data
- .always =>
- @hideSpinner()
.done =>
@options.app.unsetDefaults @model.id
@model.set 'default', !@model.get('default')
@@ -82,15 +77,5 @@ define [
@changeDefault false
- showSpinner: ->
- @$el.hide()
- jQuery(@spinner).insertBefore @$el
-
-
- hideSpinner: ->
- @$el.prev().remove()
- @$el.show()
-
-
serializeData: ->
_.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee b/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
index d2c618fa745..4f2e3eaa14c 100644
--- a/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
+++ b/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
@@ -3,7 +3,7 @@ define [
], ->
class QualityGateSidebarListItemView extends Marionette.ItemView
- tagName: 'li'
+ className: 'facet search-navigator-facet'
template: Templates['quality-gate-sidebar-list-item']
diff --git a/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee b/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee
index 063fd569a2c..7cdfbe4a14e 100644
--- a/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee
+++ b/server/sonar-web/src/main/coffee/quality-gate/views/quality-gate-sidebar-list-view.coffee
@@ -7,8 +7,7 @@ define [
) ->
class QualityGateSidebarListView extends Marionette.CollectionView
- tagName: 'ol'
- className: 'navigator-results-list'
+ className: 'search-navigator-facet-list'
itemView: QualityGateSidebarListItemView
emptyView: QualityGateSidebarListEmptyView
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-workspace-list-item.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-workspace-list-item.hbs
index 7db42af3919..75f4c415e31 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-workspace-list-item.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-workspace-list-item.hbs
@@ -23,7 +23,7 @@
<td class="coding-rule-table-meta-cell">
<div class="coding-rule-meta">
{{#notEq status 'READY'}}
- {{status}}
+ <span class="text-danger">{{status}}</span>
&nbsp;&nbsp;&nbsp;
{{/notEq}}
<a class="js-lang link-no-underline" data-lang="{{lang}}">{{langName}}</a>
diff --git a/server/sonar-web/src/main/hbs/issues/issues-filters.hbs b/server/sonar-web/src/main/hbs/issues/issues-filters.hbs
index d3e85908014..ee0f442ae96 100644
--- a/server/sonar-web/src/main/hbs/issues/issues-filters.hbs
+++ b/server/sonar-web/src/main/hbs/issues/issues-filters.hbs
@@ -23,12 +23,25 @@
<div class="button-group">
{{#if state.canManageFilters}}
{{#if filter.canModify}}
- {{#if state.changed}}<button class="js-filter-save" id="issues-filter-save">{{t 'save'}}</button>{{/if}}
+ {{#if state.changed}}
+ <button class="js-filter-save" id="issues-filter-save">{{t 'save'}}</button>
+ {{/if}}
{{/if}}
- {{#unless filter.id}}<button class="js-filter-save-as" id="issues-filter-save-as">{{t 'save_as'}}</button>{{/unless}}
- {{#if filter.id}}<button class="js-filter-copy" id="issues-filter-copy">{{t 'copy'}}</button>{{/if}}
+
+ {{#unless filter.id}}
+ <button class="js-filter-save-as" id="issues-filter-save-as">{{t 'save_as'}}</button>
+ {{/unless}}
+
+ {{#if filter.id}}
+ {{#unless state.changed}}
+ <button class="js-filter-copy" id="issues-filter-copy">{{t 'copy'}}</button>
+ {{/unless}}
+ {{/if}}
+
{{#if filter.canModify}}
- {{#if filter.id}}<button class="js-filter-edit" id="issues-filter-edit">{{t 'edit'}}</button>{{/if}}
+ {{#if filter.id}}
+ <button class="js-filter-edit" id="issues-filter-edit">{{t 'edit'}}</button>
+ {{/if}}
{{/if}}
{{/if}}
</div>
diff --git a/server/sonar-web/src/main/hbs/issues/issues-workspace-header.hbs b/server/sonar-web/src/main/hbs/issues/issues-workspace-header.hbs
index 21158f33975..e29177268b7 100644
--- a/server/sonar-web/src/main/hbs/issues/issues-workspace-header.hbs
+++ b/server/sonar-web/src/main/hbs/issues/issues-workspace-header.hbs
@@ -1,6 +1,12 @@
<div class="issues-header-component">
{{#if state.component}}
- <a class="js-back">{{t 'issues.return_to_list'}}</a>
+ <a class="js-back">{{t 'issues.return_to_list'}}</a>&nbsp;&nbsp;&nbsp;
+
+ {{#with state.component}}
+ {{qualifierIcon 'TRK'}}&nbsp;<a href="{{dashboardUrl project}}" title="{{projectName}}">{{projectName}}</a>
+ &nbsp;&nbsp;
+ {{qualifierIcon qualifier}}&nbsp;<a href="{{dashboardUrl key}}" title="{{name}}">{{name}}</a>
+ {{/with}}
{{else}}
&nbsp;
{{/if}}
diff --git a/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-condition.hbs b/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-condition.hbs
index 79973fe9a38..9be26807f78 100644
--- a/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-condition.hbs
+++ b/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-condition.hbs
@@ -27,19 +27,19 @@
{{t 'quality_gates.operator' op}}
{{/if}}
</td>
-<td width="15%" nowrap="nowrap">
+<td width="1" class="nowrap">
<i class="icon-alert-warn" title="{{t 'quality_gates.warning_tooltip'}}"></i>
{{#if canEdit}}
- <input name="warning" class="measure-input" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
+ <input name="warning" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
type="text">
{{else}}
{{warning}}
{{/if}}
</td>
-<td width="15%" nowrap="nowrap">
+<td width="1" class="nowrap">
<i class="icon-alert-error" title="{{t 'quality_gates.error_tooltip'}}"></i>
{{#if canEdit}}
- <input name="error" class="measure-input" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
+ <input name="error" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
type="text">
{{else}}
{{error}}
diff --git a/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-header.hbs b/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-header.hbs
index e84a730d379..71f7e3b06de 100644
--- a/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-header.hbs
+++ b/server/sonar-web/src/main/hbs/quality-gates/quality-gate-detail-header.hbs
@@ -1,14 +1,16 @@
-<h1 class="navigator-header-title">{{name}}</h1>
+<h2 class="search-navigator-header-component">{{name}}</h2>
{{#if canEdit}}
- <div class="navigator-header-actions button-group">
- <button id="quality-gate-rename">{{t 'rename'}}</button>
- <button id="quality-gate-copy">{{t 'copy'}}</button>
- {{#if default}}
- <button id="quality-gate-unset-as-default">{{t 'unset_as_default'}}</button>
- {{else}}
- <button id="quality-gate-set-as-default">{{t 'set_as_default'}}</button>
- {{/if}}
- <button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
+ <div class="search-navigator-header-actions">
+ <div class="button-group">
+ <button id="quality-gate-rename">{{t 'rename'}}</button>
+ <button id="quality-gate-copy">{{t 'copy'}}</button>
+ {{#if default}}
+ <button id="quality-gate-unset-as-default">{{t 'unset_as_default'}}</button>
+ {{else}}
+ <button id="quality-gate-set-as-default">{{t 'set_as_default'}}</button>
+ {{/if}}
+ <button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
+ </div>
</div>
-{{/if}} \ No newline at end of file
+{{/if}}
diff --git a/server/sonar-web/src/main/hbs/quality-gates/quality-gate-sidebar-list-item.hbs b/server/sonar-web/src/main/hbs/quality-gates/quality-gate-sidebar-list-item.hbs
index af8d93ae07b..2b8f1089eb7 100644
--- a/server/sonar-web/src/main/hbs/quality-gates/quality-gate-sidebar-list-item.hbs
+++ b/server/sonar-web/src/main/hbs/quality-gates/quality-gate-sidebar-list-item.hbs
@@ -1 +1,7 @@
-<div class="line line-nowrap">{{name}} {{#if default}}<span class="subtitle">({{t 'default'}})</span>{{/if}}</div> \ No newline at end of file
+<span class="facet-name">
+ {{name}}
+</span>
+
+{{#if default}}
+ <span class="facet-stat">{{t 'default'}}</span>
+{{/if}}
diff --git a/server/sonar-web/src/main/hbs/quality-gates/quality-gates-layout.hbs b/server/sonar-web/src/main/hbs/quality-gates/quality-gates-layout.hbs
index 22f6fba05e7..d596459b6a7 100644
--- a/server/sonar-web/src/main/hbs/quality-gates/quality-gates-layout.hbs
+++ b/server/sonar-web/src/main/hbs/quality-gates/quality-gates-layout.hbs
@@ -1,10 +1,9 @@
-<div class="navigator-content">
- <div class="navigator-side">
- <div class="navigator-actions"></div>
- <div class="navigator-results quality-gates-nav"></div>
- </div>
- <div class="navigator-main">
- <div class="navigator-header"></div>
- <div class="navigator-details"></div>
- </div>
-</div> \ No newline at end of file
+<div class="search-navigator-side quality-gates-side">
+ <div class="search-navigator-filters"></div>
+ <div class="quality-gates-results"></div>
+</div>
+
+<div class="search-navigator-workspace">
+ <div class="search-navigator-workspace-header"></div>
+ <div class="search-navigator-workspace-list"></div>
+</div>
diff --git a/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs b/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs
index 6a73836e5a3..0f239f255df 100644
--- a/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs
+++ b/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs
@@ -18,19 +18,20 @@
</td>
<td class="source-meta source-line-coverage {{#notNull coverageStatus}}source-line-{{coverageStatus}}{{/notNull}}"
- data-line-number="{{line}}">
+ data-line-number="{{line}}" {{#notNull coverageStatus}}title="{{t 'source_viewer.tooltip' coverageStatus}}" data-placement="right" data-toggle="tooltip"{{/notNull}}>
<div class="source-line-bar"></div>
</td>
{{#if ../hasDuplications}}
<td class="source-meta source-line-duplications {{#if duplicated}}source-line-duplicated{{/if}}"
- title="{{t 'source_viewer.expand_duplications'}}">
+ {{#if duplicated}}title="{{t 'source_viewer.tooltip.duplicated_line'}}" data-placement="right" data-toggle="tooltip"{{/if}}>
<div class="source-line-bar"></div>
</td>
{{#each duplications}}
<td class="source-meta source-line-duplications-extra {{#if this}}source-line-duplicated{{/if}}"
- data-index="{{this}}" data-line-number="{{line}}">
+ data-index="{{this}}" data-line-number="{{../line}}"
+ {{#if this}}title="{{t 'source_viewer.tooltip.duplicated_block'}}" data-placement="right" data-toggle="tooltip"{{/if}}>
<div class="source-line-bar"></div>
</td>
{{/each}}
@@ -43,6 +44,10 @@
{{/withFirst}}
</td>
+ <td class="source-meta source-line-filtered-container" data-line-number="{{line}}">
+ <div class="source-line-bar"></div>
+ </td>
+
<td class="source-line-code code {{#notEmpty issues}}has-issues{{/notEmpty}}" data-line-number="{{line}}">
{{#notNull code}}
<pre>{{#if code}}{{{code}}}{{else}}&nbsp;{{/if}}</pre>
diff --git a/server/sonar-web/src/main/js/coding-rules/controller.js b/server/sonar-web/src/main/js/coding-rules/controller.js
index 91e98b84639..627b7e3ec66 100644
--- a/server/sonar-web/src/main/js/coding-rules/controller.js
+++ b/server/sonar-web/src/main/js/coding-rules/controller.js
@@ -1,7 +1,7 @@
define([
- 'components/navigator/controller',
- 'coding-rules/models/rule',
- 'coding-rules/rule-details-view'
+ 'components/navigator/controller',
+ 'coding-rules/models/rule',
+ 'coding-rules/rule-details-view'
], function (Controller, Rule, RuleDetailsView) {
var $ = jQuery;
@@ -20,14 +20,16 @@ define([
fields.push('isTemplate');
fields.push('severity');
}
- return {
+ var params = {
p: this.app.state.get('page'),
ps: this.pageSize,
facets: this._facetsFromServer().join(),
- f: fields.join(),
- s: 'name',
- asc: true
+ f: fields.join()
};
+ if (this.app.state.get('query').q == null) {
+ _.extend(params, { s: 'name', asc: true });
+ }
+ return params;
},
fetchList: function (firstPage) {
diff --git a/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js b/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js
index 1de7444ac85..2d2f1f85730 100644
--- a/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js
+++ b/server/sonar-web/src/main/js/coding-rules/rule/profile-activation-view.js
@@ -29,7 +29,8 @@ define([
minimumResultsForSearch: 5
});
- var format = function (state) {
+ var that = this,
+ format = function (state) {
if (!state.id) {
return state.text;
} else {
@@ -44,6 +45,9 @@ define([
formatResult: format,
formatSelection: format
});
+ setTimeout(function () {
+ that.$('a').first().focus();
+ }, 0);
},
activate: function (e) {
diff --git a/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js b/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js
index 56937b8add9..eb1e49a5a11 100644
--- a/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js
+++ b/server/sonar-web/src/main/js/coding-rules/workspace-list-item-view.js
@@ -14,6 +14,7 @@ define([
events: {
'click': 'selectCurrent',
+ 'dblclick': 'openRule',
'click .js-rule': 'openRule',
'click .coding-rules-detail-quality-profile-activate': 'activate',
'click .coding-rules-detail-quality-profile-change': 'change',
diff --git a/server/sonar-web/src/main/js/drilldown/app.js b/server/sonar-web/src/main/js/drilldown/app.js
index 45ebac66d20..85be33f8b90 100644
--- a/server/sonar-web/src/main/js/drilldown/app.js
+++ b/server/sonar-web/src/main/js/drilldown/app.js
@@ -24,7 +24,7 @@ requirejs([
viewer.open(uuid);
if (window.drilldown.period != null) {
viewer.on('loaded', function () {
- viewer.filterLinesByDate(window.drilldown.period);
+ viewer.filterLinesByDate(window.drilldown.period, window.drilldown.periodName);
});
}
});
diff --git a/server/sonar-web/src/main/js/graphics/barchart.js b/server/sonar-web/src/main/js/graphics/barchart.js
index 5b51819bad4..b96ca95f901 100644
--- a/server/sonar-web/src/main/js/graphics/barchart.js
+++ b/server/sonar-web/src/main/js/graphics/barchart.js
@@ -74,10 +74,18 @@
.attr('height', function (d) {
return Math.floor(yScale(d.count));
})
+ .style('cursor', 'pointer')
+ .attr('data-period-start', function (d) {
+ return moment(d.val).format('YYYY-MM-DD');
+ })
+ .attr('data-period-end', function (d, i) {
+ var ending = i < data.length - 1 ? moment(data[i + 1].val).subtract(1, 'seconds') : moment();
+ return ending.format('YYYY-MM-DD');
+ })
.attr('title', function (d, i) {
var beginning = moment(d.val),
ending = i < data.length - 1 ? moment(data[i + 1].val).subtract(1, 'days') : moment();
- return d.count + ' | ' + beginning.format('LL') + ' - ' + ending.format('LL');
+ return d.count + '<br>' + beginning.format('LL') + ' – ' + ending.format('LL');
})
.attr('data-placement', 'right')
.attr('data-toggle', 'tooltip');
@@ -97,7 +105,7 @@
return text;
});
- $(this).find('[data-toggle=tooltip]').tooltip({ container: 'body' });
+ $(this).find('[data-toggle=tooltip]').tooltip({ container: 'body', html: true });
}
});
};
diff --git a/server/sonar-web/src/main/js/nav/app.js b/server/sonar-web/src/main/js/nav/app.js
index 36e52658a1d..7945b6274bb 100644
--- a/server/sonar-web/src/main/js/nav/app.js
+++ b/server/sonar-web/src/main/js/nav/app.js
@@ -40,24 +40,6 @@ define([
}
App.addInitializer(function () {
- var navHeight = $('.navbar-global').outerHeight() + $('.navbar-context').outerHeight();
- $('.page-wrapper').css('padding-top', navHeight).data('top-offset', navHeight);
- });
-
- App.addInitializer(function () {
- var that = this;
- $(window).on('keypress', function (e) {
- var tagName = e.target.tagName;
- if (tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA') {
- var code = e.keyCode || e.which;
- if (code === 63) {
- that.navbarView.showShortcutsHelp();
- }
- }
- });
- });
-
- App.addInitializer(function () {
var that = this;
$(window).on('keypress', function (e) {
var tagName = e.target.tagName;
diff --git a/server/sonar-web/src/main/js/source-viewer/viewer.js b/server/sonar-web/src/main/js/source-viewer/viewer.js
index a8ee471090d..c86bb2e0d98 100644
--- a/server/sonar-web/src/main/js/source-viewer/viewer.js
+++ b/server/sonar-web/src/main/js/source-viewer/viewer.js
@@ -51,7 +51,9 @@ define([
'click .source-line-duplications': 'showDuplications',
'click .source-line-duplications-extra': 'showDuplicationPopup',
'click .source-line-with-issues': 'onLineIssuesClick',
- 'click .source-line-number[data-line-number]': 'onLineNumberClick'
+ 'click .source-line-number[data-line-number]': 'onLineNumberClick',
+ 'mouseenter .source-line-filtered .source-line-filtered-container': 'showFilteredTooltip',
+ 'mouseleave .source-line-filtered .source-line-filtered-container': 'hideFilteredTooltip'
};
},
@@ -82,6 +84,7 @@ define([
if (this.model.has('filterLinesFunc')) {
this.filterLines(this.model.get('filterLinesFunc'));
}
+ this.$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
},
onClose: function () {
@@ -89,6 +92,11 @@ define([
return view.close();
});
this.issueViews = [];
+ this.clearTooltips();
+ },
+
+ clearTooltips: function () {
+ this.$('[data-toggle="tooltip"]').tooltip('destroy');
},
onLoaded: function () {
@@ -361,6 +369,7 @@ define([
showDuplications: function (e) {
var that = this,
lineNumber = $(e.currentTarget).closest('.source-line').data('line-number');
+ this.clearTooltips();
this.requestDuplications().done(function () {
that.render();
that.$el.addClass('source-duplications-expanded');
@@ -620,12 +629,26 @@ define([
});
},
- filterLinesByDate: function (date) {
+ filterLinesByDate: function (date, label) {
var sinceDate = moment(date).toDate();
+ this.sinceLabel = label;
this.filterLines(function (line) {
var scmDate = moment(line.scmDate).toDate();
return scmDate >= sinceDate;
});
+ },
+
+ showFilteredTooltip: function (e) {
+ $(e.currentTarget).tooltip({
+ container: 'body',
+ placement: 'right',
+ title: tp('source_viewer.tooltip.new_code', this.sinceLabel),
+ trigger: 'manual'
+ }).tooltip('show');
+ },
+
+ hideFilteredTooltip: function (e) {
+ $(e.currentTarget).tooltip('destroy');
}
});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/quality-gates-spec.js b/server/sonar-web/src/main/js/tests/e2e/tests/quality-gates-spec.js
index f068858dd67..6655b4eb9ac 100644
--- a/server/sonar-web/src/main/js/tests/e2e/tests/quality-gates-spec.js
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/quality-gates-spec.js
@@ -1,36 +1,42 @@
+/* global casper:false */
+
var lib = require('../lib');
lib.initMessages();
lib.changeWorkingDirectory('quality-gates-spec');
-casper.test.begin('Quality Gates', function suite(test) {
- casper.start(lib.buildUrl('quality-gates'), function() {
- lib.setDefaultViewport();
-
- lib.mockRequest('/api/l10n/index', '{}');
- lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
- lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
- });
-
- casper.waitWhileSelector("div#quality-gates-loader", function() {
-
- casper.waitForSelector('li.active', function() {
- test.assertElementCount('li.active', 1);
- test.assertSelectorHasText('ol.navigator-results-list li', 'Default Gate');
- });
-
- casper.waitForSelector('div.navigator-header', function() {
- test.assertSelectorHasText('div.navigator-header h1', 'Default Gate');
- });
-
- casper.waitForSelector('table.quality-gate-conditions tbody tr:nth-child(9)', function() {
- test.assertElementCount('table.quality-gate-conditions tbody tr', 9);
- });
- });
-
- casper.run(function() {
- test.done();
- });
+casper.test.begin('Quality Gates', function suite (test) {
+ casper
+ .start(lib.buildUrl('quality-gates'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
+ lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
+ lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.active', function () {
+ test.assertElementCount('.active', 1);
+ test.assertSelectorHasText('.search-navigator-side .active', 'Default Gate');
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('.search-navigator-workspace-header', function () {
+ test.assertSelectorHasText('.search-navigator-workspace-header', 'Default Gate');
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('table.quality-gate-conditions tbody tr:nth-child(9)', function () {
+ test.assertElementCount('table.quality-gate-conditions tbody tr', 9);
+ });
+ })
+
+ .run(function () {
+ test.done();
+ });
});
diff --git a/server/sonar-web/src/main/js/tests/e2e/views/quality-gates.jade b/server/sonar-web/src/main/js/tests/e2e/views/quality-gates.jade
index 1a10f0b5b34..ac1a4871c1b 100644
--- a/server/sonar-web/src/main/js/tests/e2e/views/quality-gates.jade
+++ b/server/sonar-web/src/main/js/tests/e2e/views/quality-gates.jade
@@ -7,5 +7,4 @@ block header
block body
#content
- #quality-gate-page-loader.navigator-page-loader
- i.spinner
+ .search-navigator#quality-gates
diff --git a/server/sonar-web/src/main/less/components/issues.less b/server/sonar-web/src/main/less/components/issues.less
index 8177f088984..1d16cffd90a 100644
--- a/server/sonar-web/src/main/less/components/issues.less
+++ b/server/sonar-web/src/main/less/components/issues.less
@@ -133,7 +133,7 @@
.issue-meta-label {
display: inline-block;
vertical-align: top;
- max-width: 110px;
+ max-width: 180px;
.text-ellipsis;
}
diff --git a/server/sonar-web/src/main/less/components/navbar.less b/server/sonar-web/src/main/less/components/navbar.less
index 2c8da63e581..605f50f3d61 100644
--- a/server/sonar-web/src/main/less/components/navbar.less
+++ b/server/sonar-web/src/main/less/components/navbar.less
@@ -5,9 +5,8 @@
@navbarGlobalBackground: #262626;
@navbarContextBackground: @barBackgroundColor;
-@navbarHeight: 30px;
@navbarLineHeight: 20px;
-@navbarTopPadding: (@navbarHeight - @navbarLineHeight) / 2;
+@navbarTopPadding: (@navbarGlobalHeight - @navbarLineHeight) / 2;
.navbar, [class^="navbar-"], [class*=" navbar-"] {
.box-sizing(border-box);
@@ -18,7 +17,7 @@
left: 0;
right: 0;
.clearfix;
- height: @navbarHeight;
+ height: @navbarGlobalHeight;
}
.navbar-fade {
@@ -184,9 +183,9 @@
.navbar-context {
- top: @navbarHeight;
+ top: @navbarGlobalHeight;
z-index: 498;
- height: 57px;
+ height: @navbarContextHeight;
background-color: @navbarContextBackground;
.nav-tabs {
@@ -194,8 +193,8 @@
}
.navbar-nav > li > a {
- padding-top: 2px;
- padding-bottom: 2px;
+ padding-top: 3px;
+ padding-bottom: 3px;
}
}
@@ -205,7 +204,7 @@
}
.navbar-context-meta {
- line-height: @navbarHeight;
+ line-height: @navbarGlobalHeight;
padding: 0 10px;
color: @secondFontColor;
font-size: @smallFontSize;
diff --git a/server/sonar-web/src/main/less/components/navigator/base.less b/server/sonar-web/src/main/less/components/navigator/base.less
index 9b448f74e4b..29f7d7f2dba 100644
--- a/server/sonar-web/src/main/less/components/navigator/base.less
+++ b/server/sonar-web/src/main/less/components/navigator/base.less
@@ -21,7 +21,6 @@
.navigator-filters {
position: relative;
- margin: @navigatorPadding;
border: 1px solid @navigatorBorderLightColor;
.box-sizing(border-box);
}
@@ -72,7 +71,7 @@
.navigator-details {
position: relative;
- margin: 0 @navigatorPadding @navigatorPadding;
+ margin: @navigatorPadding 0 0 0;
}
.navigator-resizer {
@@ -95,7 +94,6 @@
.measures-page {
.navigator-details { overflow: visible; }
- .page { padding: 0 0 0 @navigatorPadding; }
}
diff --git a/server/sonar-web/src/main/less/components/navigator/filters.less b/server/sonar-web/src/main/less/components/navigator/filters.less
index 7c56f015d96..67fbac25279 100644
--- a/server/sonar-web/src/main/less/components/navigator/filters.less
+++ b/server/sonar-web/src/main/less/components/navigator/filters.less
@@ -312,9 +312,7 @@
}
.navigator-filter-favorite {
- position: absolute;
- top: -@navigatorPadding - @navigatorFiltersHeight;
- left: 0;
+ position: relative;
}
.navigator-filter-favorite-toggle {
diff --git a/server/sonar-web/src/main/less/components/page.less b/server/sonar-web/src/main/less/components/page.less
index 207bbc31737..05c57de58a9 100644
--- a/server/sonar-web/src/main/less/components/page.less
+++ b/server/sonar-web/src/main/less/components/page.less
@@ -4,6 +4,7 @@
.page {
+ .clearfix;
position: relative;
padding: 10px;
}
@@ -17,6 +18,19 @@
.box-sizing(border-box);
}
+.page-wrapper-global {
+ padding-top: @navbarGlobalHeight;
+}
+
+.page-wrapper-context {
+ padding-top: @navbarGlobalHeight + @navbarContextHeight;
+}
+
+.page-simple {
+ margin: 50px 180px 0;
+ text-align: left;
+}
+
.page-header {
.clearfix;
margin-bottom: 10px;
diff --git a/server/sonar-web/src/main/less/components/source.less b/server/sonar-web/src/main/less/components/source.less
index 361634ac603..bce28e3441a 100644
--- a/server/sonar-web/src/main/less/components/source.less
+++ b/server/sonar-web/src/main/less/components/source.less
@@ -26,6 +26,7 @@
.source-line-coverage,
.source-line-duplications,
.source-line-duplications-extra,
+ .source-line-filtered-container,
.source-line-scm {
border-color: darken(@barBackgroundColor, 4%);
background-color: darken(@barBackgroundColor, 4%);
@@ -43,6 +44,7 @@
.source-line-coverage,
.source-line-duplications,
.source-line-duplications-extra,
+ .source-line-filtered-container,
.source-line-scm {
border-color: #fdf190 !important;
background-color: #fdf190;
@@ -60,9 +62,8 @@
}
.source-line-filtered {
- .source-line-code {
- border-left: 4px solid @lightBlue;
- padding-left: 6px;
+ .source-line-filtered-container {
+ background-color: @lightBlue !important;
}
}
@@ -157,6 +158,10 @@
}
}
+.source-line-filtered-container {
+ background-color: @barBackgroundColor;
+}
+
.source-line-scm {
padding: 0 5px;
background-color: @barBackgroundColor;
diff --git a/server/sonar-web/src/main/less/components/ui.less b/server/sonar-web/src/main/less/components/ui.less
index 311a2ca8328..418fe0a7896 100644
--- a/server/sonar-web/src/main/less/components/ui.less
+++ b/server/sonar-web/src/main/less/components/ui.less
@@ -101,164 +101,6 @@ a.active-link {
-/*
- * Inputs
- */
-
-input[type=text],
-input[type=password],
-input[type=email],
-input[type=search],
-textarea {
- border: 1px solid @darkGrey;
- .box-sizing(border-box);
- background: #fff;
- color: @baseFontColor;
- .trans(border-color);
-
- &:active,
- &:focus {
- border-color: @highlighted;
- box-shadow: none;
- outline: none;
- }
-
- &.invalid { border-color: @red; }
-}
-
-input[type=text],
-input[type=password],
-input[type=email],
-input[type=search] {
- height: @formControlHeight;
- padding: 0 3px;
-}
-
-input[type=search] {
- -webkit-appearance: none;
-}
-
-textarea {
- padding: 3px;
-}
-
-button,
-.button,
-input[type=submit],
-input[type=button] {
- display: inline-block;
- vertical-align: baseline;
- height: @formControlHeight;
- margin: 0 1px;
- padding: 0 10px;
-
- border: 1px solid @darkGrey;
- .box-sizing(border-box);
-
- background: #f4f4f4;
-
- color: @baseFontColor;
- font-weight: bold;
- font-size: @baseFontSize;
- text-align: center;
- text-decoration: none;
-
- cursor: pointer;
- outline: none;
- .trans(border-color);
-
- &:hover, &.active {
- border-color: #5281a0;
- background: #4b9fd5;
- color: #fff;
- }
-
- &:active {
- border-color: #2790c0;
- background: #78bdea;
- color: #fff;
- }
-
- &:focus {
- border-color: @highlighted;
- }
-
- &[disabled],
- &[disabled]:hover,
- &[disabled]:active,
- &[disabled]:focus {
- color: #bbb;
- border-color: #ddd;
- background: #ebebeb;
- cursor: default;
- }
-}
-
-.button { line-height: @formControlHeight; }
-
-.button-red {
- &:hover, &:focus {
- border-color: #900;
- background: lighten(#900, 10%);
- color: #fff;
- }
-
- &:active {
- border-color: #900;
- background: lighten(#900, 20%);
- }
-}
-
-.button-clean,
-.button-clean:hover,
-.button-clean:focus {
- margin: 0;
- padding: 0;
- border: none;
- background: transparent;
- color: @baseFontColor;
-}
-
-.button-group {
- display: inline-block;
- vertical-align: middle;
- font-size: 0;
- white-space: nowrap;
-
- & > button,
- & > .button {
- position: relative;
- z-index: 2;
- display: inline-block;
- vertical-align: middle;
- margin: 0;
- padding: 2px 8px;
- font-size: @smallFontSize;
- font-weight: normal;
- cursor: pointer;
-
- &:hover, &:focus, &:active, &.active {
- z-index: 3;
- }
- }
-
- & > .button { line-height: 16px; }
-
- & > button + button,
- & > button + .button,
- & > .button + button,
- & > .button + .button {
- margin-left: -1px;
- }
-
- & > a:not(.button) {
- vertical-align: middle;
- margin: 0 8px;
- font-size: @smallFontSize;
- }
-}
-
-
.message-notice {
display: block;
padding: 5px 8px;
@@ -466,6 +308,7 @@ input[type=button] {
}
.nav-tabs {
+ padding-top: 1px;
border-bottom: 1px solid @barBorderColor;
> li {
diff --git a/server/sonar-web/src/main/less/init.less b/server/sonar-web/src/main/less/init.less
index 86dd9fc5b3a..c2fe9052204 100644
--- a/server/sonar-web/src/main/less/init.less
+++ b/server/sonar-web/src/main/less/init.less
@@ -3,5 +3,6 @@
@import "init/links";
@import "init/tables";
@import "init/lists";
+@import "init/forms";
@import "init/icons";
@import "init/misc";
diff --git a/server/sonar-web/src/main/less/init/forms.less b/server/sonar-web/src/main/less/init/forms.less
new file mode 100644
index 00000000000..9f8be907f17
--- /dev/null
+++ b/server/sonar-web/src/main/less/init/forms.less
@@ -0,0 +1,164 @@
+@import (reference) "../variables";
+@import (reference) "../mixins";
+@import (reference) "../components/ui";
+
+/*
+ * Inputs
+ */
+
+input[type=text],
+input[type=password],
+input[type=email],
+input[type=search],
+textarea {
+ border: 1px solid @darkGrey;
+ .box-sizing(border-box);
+ background: #fff;
+ color: @baseFontColor;
+ .trans(border-color);
+
+ &:active,
+ &:focus {
+ border-color: @highlighted;
+ box-shadow: none;
+ outline: none;
+ }
+
+ &.invalid { border-color: @red; }
+}
+
+input[type=text],
+input[type=password],
+input[type=email],
+input[type=search] {
+ height: @formControlHeight;
+ padding: 0 3px;
+}
+
+input[type=search] {
+ -webkit-appearance: none;
+}
+
+textarea {
+ padding: 3px;
+}
+
+button,
+.button,
+input[type=submit],
+input[type=button] {
+ display: inline-block;
+ vertical-align: baseline;
+ height: @formControlHeight;
+ margin: 0 1px;
+ padding: 0 10px;
+
+ border: 1px solid @darkGrey;
+ .box-sizing(border-box);
+
+ background: #f4f4f4;
+
+ color: @baseFontColor;
+ font-weight: bold;
+ font-size: @baseFontSize;
+ text-align: center;
+ text-decoration: none;
+
+ cursor: pointer;
+ outline: none;
+ .trans(border-color);
+
+ &:hover, &.active {
+ border-color: #5281a0;
+ background: #4b9fd5;
+ color: #fff;
+ }
+
+ &:active {
+ border-color: #2790c0;
+ background: #78bdea;
+ color: #fff;
+ }
+
+ &:focus {
+ border-color: @highlighted;
+ }
+
+ &[disabled],
+ &[disabled]:hover,
+ &[disabled]:active,
+ &[disabled]:focus {
+ color: #bbb;
+ border-color: #ddd;
+ background: #ebebeb;
+ cursor: default;
+ }
+}
+
+.button { line-height: @formControlHeight; }
+
+.button-red {
+ &:hover, &:focus {
+ border-color: #900;
+ background: lighten(#900, 10%);
+ color: #fff;
+ }
+
+ &:active {
+ border-color: #900;
+ background: lighten(#900, 20%);
+ }
+}
+
+.button-clean,
+.button-clean:hover,
+.button-clean:focus {
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent;
+ color: @baseFontColor;
+}
+
+.button-group {
+ display: inline-block;
+ vertical-align: middle;
+ font-size: 0;
+ white-space: nowrap;
+
+ & > button,
+ & > .button {
+ position: relative;
+ z-index: 2;
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0;
+ padding: 2px 8px;
+ font-size: @smallFontSize;
+ font-weight: normal;
+ cursor: pointer;
+
+ &:hover, &:focus, &:active, &.active {
+ z-index: 3;
+ }
+ }
+
+ & > .button { line-height: 16px; }
+
+ & > button + button,
+ & > button + .button,
+ & > .button + button,
+ & > .button + .button {
+ margin-left: -1px;
+ }
+
+ & > a:not(.button) {
+ vertical-align: middle;
+ margin: 0 8px;
+ font-size: @smallFontSize;
+ }
+}
+
+.input-small {
+ width: 80px;
+}
diff --git a/server/sonar-web/src/main/less/init/type.less b/server/sonar-web/src/main/less/init/type.less
index 8a531cde54f..b6afe641785 100644
--- a/server/sonar-web/src/main/less/init/type.less
+++ b/server/sonar-web/src/main/less/init/type.less
@@ -98,3 +98,7 @@ small,
.text-justify {
text-align: justify;
}
+
+.text-danger {
+ color: @red;
+}
diff --git a/server/sonar-web/src/main/less/pages/quality-gates.less b/server/sonar-web/src/main/less/pages/quality-gates.less
index 9734d8f5b8e..eea7e0bf0d0 100644
--- a/server/sonar-web/src/main/less/pages/quality-gates.less
+++ b/server/sonar-web/src/main/less/pages/quality-gates.less
@@ -2,6 +2,14 @@
@import (reference) "../mixins";
@import (reference) "../components/navigator/config";
+.quality-gates-side {
+ background-color: @barBackgroundColor;
+}
+
+.quality-gates-results {
+ padding: 5px 0;
+}
+
@qualityGateSidebarWidth: 230px;
.quality-gates-navigator {
diff --git a/server/sonar-web/src/main/less/sonar-colorizer.less b/server/sonar-web/src/main/less/sonar-colorizer.less
index e3b2130c298..5560393ccbc 100644
--- a/server/sonar-web/src/main/less/sonar-colorizer.less
+++ b/server/sonar-web/src/main/less/sonar-colorizer.less
@@ -1,3 +1,6 @@
+@import (reference) 'variables';
+@import (reference) 'mixins';
+
/* for example java annotations */
.code .a {
color: #808000;
@@ -5,7 +8,7 @@
/* constants */
.code .c {
color: #660E80;
- font-style: italic;
+ font-style: normal;
font-weight: bold;
}
/* javadoc */
@@ -25,13 +28,13 @@
}
/* keyword */
.code .k {
- color: #000080;
- font-weight: bold;
+ color: saturate(@darkBlue, 50%);
+ font-weight: 600;
}
/* string */
.code .s {
- color: #008000;
- font-weight: bold;
+ color: saturate(@red, 0%);
+ font-weight: normal;
}
/* keyword light*/
.code .h {
diff --git a/server/sonar-web/src/main/less/variables.less b/server/sonar-web/src/main/less/variables.less
index 0857574ca68..1cc10911613 100644
--- a/server/sonar-web/src/main/less/variables.less
+++ b/server/sonar-web/src/main/less/variables.less
@@ -127,4 +127,6 @@
* Page
*/
+@navbarGlobalHeight: 30px;
+@navbarContextHeight: 60px;
@pageFooterHeight: 60px;
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb
index ebf29970d09..e6d4696c397 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/events_controller.rb
@@ -51,7 +51,7 @@ class Api::EventsController < Api::ApiController
end
if from
conditions<<'event_date>=:from'
- values[:from]=from
+ values[:from]=from.to_i*1000
end
to=nil
@@ -62,7 +62,7 @@ class Api::EventsController < Api::ApiController
end
if to
conditions<<'event_date<=:to'
- values[:to]=to
+ values[:to]=to.to_i*1000
end
events=Event.find(:all, :conditions => [conditions.join(' AND '), values], :order => 'event_date DESC')
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
index 23d172dde89..3b14a29da01 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
@@ -107,6 +107,11 @@ class IssueController < ApplicationController
end
end
+ def show
+ # the redirect is needed for the backward compatibility with eclipse plugin
+ redirect_to :action => 'search', :anchor => 'issues=' + params[:id]
+ end
+
#
#
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb
index 3b00a8a16b0..3ac6eba4b8d 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb
@@ -371,9 +371,13 @@ class ProfilesController < ApplicationController
# GET /profiles/compare?id1=<profile1 id>&id2=<profile2 id>
def compare
@profiles = Profile.all(:order => 'language asc, name')
- if params[:id1].present? && params[:id2].present?
- @profile1 = Profile.find(params[:id1])
- @profile2 = Profile.find(params[:id2])
+ id1 = params[:id1]
+ id2 = params[:id2]
+ if id1.present? && id2.present? && id1.respond_to?(:to_i) && id2.respond_to?(:to_i)
+ @id1 = params[:id1].to_i
+ @id2 = params[:id2].to_i
+ @profile1 = Profile.find(id1)
+ @profile2 = Profile.find(id2)
arules1 = ActiveRule.all(:include => [{:active_rule_parameters => :rules_parameter}, :rule],
:conditions => ['active_rules.profile_id=?', @profile1.id])
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb
index 4831457ad1a..e384ae00248 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/resource_controller.rb
@@ -25,6 +25,7 @@ class ResourceController < ApplicationController
helper :dashboard
helper UsersHelper
+ # DO NOT REMOVE - used by eclipse plugin
def index
require_parameters 'id'
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/models/event.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/models/event.rb
index 97d5c277e7a..d5349ebaad3 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/models/event.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/models/event.rb
@@ -27,6 +27,27 @@ class Event < ActiveRecord::Base
belongs_to :snapshot
before_save :populate_snapshot
+
+ def created_at
+ long_to_date(:created_at)
+ end
+
+ def created_at=(date)
+ write_attribute(:created_at, date.to_i*1000)
+ end
+
+ def event_date
+ long_to_date(:event_date)
+ end
+
+ def event_date=(date)
+ write_attribute(:event_date, date.to_i*1000)
+ end
+
+ def long_to_date(attribute)
+ date_in_long = read_attribute(attribute)
+ Time.at(date_in_long/1000) if date_in_long
+ end
def fullname
if category
@@ -59,10 +80,8 @@ class Event < ActiveRecord::Base
return false
end
- #
- # TODO: Remove this code when everything has been checked on the Event handling, both on the UI and the WS API
- #
def populate_snapshot
+ self.created_at=DateTime.now unless self.created_at
self.snapshot=Snapshot.snapshot_by_date(resource_id, event_date) unless self.snapshot
end
end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/all_projects/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/all_projects/index.html.erb
index a1db3f3e830..31f11fd94fe 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/all_projects/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/all_projects/index.html.erb
@@ -1,21 +1,23 @@
<% if @filter.rows %>
+ <div class="page">
+ <header class="page-header">
+ <h1 class="page-title"><%= message('qualifiers.all.' + @qualifier) -%></h1>
+ </header>
- <h1 class="marginbottom10"><%= message('qualifiers.all.' + @qualifier) -%></h1>
+ <div id="all-projects">
- <div id="all-projects">
+ <% if @filter.security_exclusions %>
+ <p class="notes"><%= message('all-projects.results_not_display_due_to_security') -%></p>
+ <% end %>
- <% if @filter.security_exclusions %>
- <p class="notes"><%= message('all-projects.results_not_display_due_to_security') -%></p>
- <% end %>
+ <%
+ display_favourites = logged_in?
+ colspan = 5
+ colspan += 1 if display_favourites
+ %>
- <%
- display_favourites = logged_in?
- colspan = 5
- colspan += 1 if display_favourites
- %>
-
- <table class="data" id="all-projects-table">
- <thead>
+ <table class="data" id="all-projects-table">
+ <thead>
<tr>
<% if display_favourites %>
<th class="thin" style></th>
@@ -29,16 +31,16 @@
<th></th>
<th></th>
</tr>
- </thead>
-
- <tbody>
+ </thead>
+
+ <tbody>
<% @filter.rows.each do |row| %>
<tr class="thin <%= cycle 'even', 'odd' -%>">
<% if display_favourites %>
<td class="thin"><%= link_to_favourite(row.snapshot.resource) -%></td>
<% end %>
<td class="nowrap">
- <%= qualifier_icon(row.snapshot)-%> <%= link_to(h(row.snapshot.resource.name(true)), {:controller => 'dashboard', :id => row.snapshot.resource_id}, :title => h(row.snapshot.resource.key)) -%>
+ <%= qualifier_icon(row.snapshot) -%> <%= link_to(h(row.snapshot.resource.name(true)), {:controller => 'dashboard', :id => row.snapshot.resource_id}, :title => h(row.snapshot.resource.key)) -%>
</td>
<td class="sep"></td>
<td>
@@ -46,32 +48,34 @@
</td>
<td class="sep"></td>
<td class="nowrap right">
- <%
+ <%
if row.links
row.links.select { |link| link.href.start_with?('http') }.each do |link|
%>
- <a target="_blank" href="<%= link.href -%>" class="icon-<%= link.link_type -%>"></a>
- <%
+ <a target="_blank" href="<%= link.href -%>" class="icon-<%= link.link_type -%>"></a>
+ <%
end
- end
- %>
+ end
+ %>
</td>
</tr>
<% end %>
-
+
<% if @filter.rows.empty? %>
<tr class="even">
<td colspan="<%= colspan -%>"><%= message 'no_data' -%></td>
</tr>
<% end %>
- </tbody>
+ </tbody>
+
+ <%= table_pagination(@filter.pagination, :colspan => colspan) { |label, page_id|
+ link_to(label, :action => 'index', :qualifier => h(@qualifier), :asc => h(@filter.criteria[:asc]), :page => page_id)
+ }
+ -%>
+
+ </table>
+ </div>
- <%= table_pagination(@filter.pagination, :colspan => colspan) { |label, page_id|
- link_to(label, :action => 'index', :qualifier => h(@qualifier), :asc => h(@filter.criteria[:asc]), :page => page_id)
- }
- -%>
-
- </table>
</div>
-
+
<% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb
index 30c43cc9aa0..3bb4a42d099 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/configure.html.erb
@@ -1,4 +1,4 @@
-<div id="dashboard">
+<div class="page" id="dashboard">
<%= render :partial => 'header', :locals => {:back => true} %>
<div id="configure">
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/no_dashboard.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/no_dashboard.html.erb
index cc4f431d7bd..ecef10c64d7 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/no_dashboard.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/no_dashboard.html.erb
@@ -12,5 +12,5 @@
key: '<%= @resource.key -%>'
};
document.querySelector('.navbar-context').remove();
+ jQuery('.page-wrapper-context').addClass('page-wrapper-global').removeClass('page-wrapper-context');
</script>
-
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb
index eaf925d5c44..24864f5bcf5 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb
@@ -125,7 +125,8 @@
metric: <% if @metric %>'<%= @metric.key -%>'<% else %>null<% end %>,
rule: null,
severity: null,
- period: <% if @period %>'<%= @snapshot.period_datetime(@period) -%>'<% else %>null<% end %>
+ period: <% if @period %>'<%= @snapshot.period_datetime(@period) -%>'<% else %>null<% end %>,
+ periodName: <% if @period %>'<%= period_label(@snapshot, @period) -%>'<% else %>null<% end %>,
};
</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
index d426dd667d9..be930090850 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
@@ -4,12 +4,13 @@
selected_section = Navigation::SECTION_HOME
end
@project=@resource unless @project || selected_section==Navigation::SECTION_HOME
+ has_context_nav = selected_section==Navigation::SECTION_RESOURCE || selected_section==Navigation::SECTION_CONFIGURATION
period_param = "period=#{u(params[:period])}" if params[:period]
%>
-<div class="page-wrapper" id="container">
+<div class="page-wrapper <% if has_context_nav %>page-wrapper-context<% else %>page-wrapper-global<% end %>" id="container">
<nav class="navbar navbar-global page-container" id="global-navigation"></nav>
- <% if selected_section==Navigation::SECTION_RESOURCE || selected_section==Navigation::SECTION_CONFIGURATION %>
+ <% if has_context_nav %>
<nav class="navbar navbar-context page-container" id="context-navigation"></nav>
<% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/nonav.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/nonav.html.erb
index 7637ea77cf9..2c76816b05c 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/nonav.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/nonav.html.erb
@@ -1,7 +1,7 @@
<%= render :partial => 'layouts/head' %>
<div>
<div id="bd">
- <div id="nonav">
+ <div id="nonav" class="page-simple">
<%= yield %>
</div>
</div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/maintenance/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/maintenance/index.html.erb
index 8571ebb1edd..87c55d92872 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/maintenance/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/maintenance/index.html.erb
@@ -4,14 +4,9 @@
border: 2px solid #4B9FD5;
background-color: #CAE3F2;
}
-#maintenancelogo {
- float: right;
- padding-left: 20px;
-}
</style>
<div id="maintenance">
-<div id="maintenancelogo"><a href="http://www.sonarqube.org"><%= image_tag('logo.png', :class => 'png') -%></a></div>
<h1>SonarQube is under maintenance. <a href="<%= ApplicationController.root_context -%>/">Please check back later.</a></h1>
<p>Whilst waiting, you might want to check <a href="http://sonar-plugins.codehaus.org">new plugins</a> to extend the current functionality. </p><p>If you are an administrator and have no idea why this message is showing, you should read the <a href="http://redirect.sonarsource.com/doc/upgrading.html">upgrade guide</a>.</p>
</div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_body.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_body.html.erb
index d1370ecc8b8..ac365ce8836 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_body.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_body.html.erb
@@ -6,7 +6,5 @@
<p class="notes"><%= message('results_not_display_due_to_security') -%></p>
<% end %>
- <div class="page">
- <%= render :partial => 'measures/display', :locals => {:filter => @filter, :edit_mode => edit_mode, :widget_id => nil} -%>
- </div>
+ <%= render :partial => 'measures/display', :locals => {:filter => @filter, :edit_mode => edit_mode, :widget_id => nil} -%>
<% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_header.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_header.html.erb
index de527f0ed81..fa098e97750 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_header.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_search_header.html.erb
@@ -46,20 +46,20 @@
<% end %>
<% end %>
</div>
- </div>
- <% unless edit_mode %>
- <% if @filter.display %>
- <div class="navigator-header-actions button-group">
- <button id="change-display" onclick="window.location='<%= url_for @filter.criteria.merge({:action => 'search', :edit => true, :id => @filter.id}) -%>';"><%= message("measure_filter.#{@filter.display.key}.change") -%></button>
- </div>
+ <% unless edit_mode %>
+ <% if @filter.display %>
+ <div class="button-group">
+ <button id="change-display" onclick="window.location='<%= url_for @filter.criteria.merge({:action => 'search', :edit => true, :id => @filter.id}) -%>';"><%= message("measure_filter.#{@filter.display.key}.change") -%></button>
+ </div>
+ <% end %>
<% end %>
- <% end %>
+ </div>
<% if @filter.description.present? %>
- <div id="filter-description" class="navigator-header-description"><%= h @filter.description -%></div>
+ <div id="filter-description" class="page-description"><%= h @filter.description -%></div>
<% end %>
<% else %>
- <h1 class="navigator-header-title"><%= message('layout.measures') -%></h1>
+ <h1 class="page-title"><%= message('layout.measures') -%></h1>
<% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
index bf9ad6c0345..bbdaaeb8b22 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
@@ -3,10 +3,10 @@
<% end %>
-<div class="navigator">
- <div class="navigator-header">
+<div class="page">
+ <header class="page-header">
<%= render :partial => 'search_header' -%>
- </div>
+ </header>
<div class="navigator-filters"></div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb
index 5f0578dc5cc..54051821fc1 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/compare.html.erb
@@ -9,12 +9,12 @@
<form method="GET" class="marginbottom10">
<select name="id1" class="small">
<option value=""></option>
- <%= options_for_profiles(@profiles, params[:id1]) %>
+ <%= options_for_profiles(@profiles, @id1) %>
</select>
<select name="id2" class="small">
<option value=""></option>
- <%= options_for_profiles(@profiles, params[:id2]) %>
+ <%= options_for_profiles(@profiles, @id2) %>
</select>
<input type="submit" value="<%= message('compare') -%>" class="small" id="submit-compare"/>
</form>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb
index 4a1ee1605e2..181ab0efae2 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb
@@ -2,6 +2,4 @@
<script>require(['quality-gate/app']);</script>
<% end %>
-<div id="quality-gate-page-loader" class="navigator-page-loader">
- <i class="spinner"></i>
-</div>
+<div class="search-navigator" id="quality-gates"></div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_groups.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_groups.html.erb
index 186d8b2da26..a6ce4faf816 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_groups.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_groups.html.erb
@@ -1,5 +1,5 @@
<div class="modal-head">
- <h2><%= @project ? "Edit Permission #{message("projects_role.#{@role}")} For: " + h(@project.name) : "Edit Global Permission: #{message("global_permissions.#{@role}")}" -%></h2>
+ <h2><%= @project ? "Edit Permission #{message("projects_role.#{h @role}")} For: " + h(@project.name) : "Edit Global Permission: #{message("global_permissions.#{h @role}")}" -%></h2>
</div>
<div class="modal-body">
@@ -21,12 +21,12 @@
}
return label;
},
- searchUrl: baseUrl + '/permissions/search_groups?permission=<%= @role -%><%= @project ? "&component=" + @project.key : "" -%>',
+ searchUrl: baseUrl + '/permissions/search_groups?permission=<%= u @role -%><%= @project ? "&component=" + u(@project.key) : "" -%>',
selectUrl: baseUrl + '/api/permissions/add',
deselectUrl: baseUrl + '/api/permissions/remove',
extra: {
- permission: '<%= @role -%>'
- <%= @project ? ", component: '" + @project.key + "'" : "" %>
+ permission: '<%= escape_javascript @role -%>'
+ <%= @project ? ", component: '" + escape_javascript(@project.key) + "'" : "" %>
},
selectParameter: 'group',
selectParameterValue: 'name',
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_users.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_users.html.erb
index 3e2bad239c8..1c08cbf5269 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_users.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/_edit_users.html.erb
@@ -1,5 +1,5 @@
<div class="modal-head">
- <h2><%= @project ? "Edit Permission #{message("projects_role.#{@role}")} For: " + h(@project.name) : "Edit Global Permission: #{message("global_permissions.#{@role}")}" -%></h2>
+ <h2><%= @project ? "Edit Permission #{message("projects_role.#{h @role }")} For: " + h(@project.name) : "Edit Global Permission: #{message("global_permissions.#{h @role}")}" -%></h2>
</div>
<div class="modal-body">
@@ -15,12 +15,12 @@
el: '#select-users-permissions',
width: '100%',
format: function (item) { return item.name + ' <div class="subtitle">' + item.login + '</div>'; },
- searchUrl: baseUrl + '/permissions/search_users?permission=<%= @role -%><%= @project ? "&component=" + @project.key : "" -%>',
+ searchUrl: baseUrl + '/permissions/search_users?permission=<%= u @role -%><%= @project ? "&component=" + u(@project.key) : "" -%>',
selectUrl: baseUrl + '/api/permissions/add',
deselectUrl: baseUrl + '/api/permissions/remove',
extra: {
- permission: '<%= @role -%>'
- <%= @project ? ", component: '" + @project.key + "'" : "" %>
+ permission: '<%= escape_javascript @role -%>'
+ <%= @project ? ", component: '" + escape_javascript(@project.key) + "'" : "" %>
},
selectParameter: 'user',
selectParameterValue: 'login',
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/791_add_events_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/791_add_events_long_dates.rb
new file mode 100644
index 00000000000..ae4f097c7f0
--- /dev/null
+++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/791_add_events_long_dates.rb
@@ -0,0 +1,29 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 5.1
+#
+class AddEventsLongDates < ActiveRecord::Migration
+ def self.up
+ add_column 'events', :event_date_ms, :big_integer, :null => true
+ add_column 'events', :created_at_ms, :big_integer, :null => true
+ end
+end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/792_feed_events_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/792_feed_events_long_dates.rb
new file mode 100644
index 00000000000..b058d46e559
--- /dev/null
+++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/792_feed_events_long_dates.rb
@@ -0,0 +1,29 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 5.1
+#
+class FeedEventsLongDates < ActiveRecord::Migration
+ def self.up
+ execute_java_migration('org.sonar.server.db.migrations.v51.FeedEventsLongDates')
+ end
+end
+
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/793_rename_events_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/793_rename_events_long_dates.rb
new file mode 100644
index 00000000000..a46e2a5c488
--- /dev/null
+++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/793_rename_events_long_dates.rb
@@ -0,0 +1,34 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 5.1
+#
+class RenameEventsLongDates < ActiveRecord::Migration
+ def self.up
+ remove_column 'events', 'created_at'
+ remove_column 'events', 'event_date'
+ rename_column 'events', 'created_at_ms', 'created_at'
+ rename_column 'events', 'event_date_ms', 'event_date'
+ change_column 'events', 'created_at', :big_integer, :null => false
+ change_column 'events', 'event_date', :big_integer, :null => false
+ end
+end
+
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/794_add_characteristic_usability_and_sub_characteristics_compliance.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/794_add_characteristic_usability_and_sub_characteristics_compliance.rb
new file mode 100644
index 00000000000..4c65c3c3864
--- /dev/null
+++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/794_add_characteristic_usability_and_sub_characteristics_compliance.rb
@@ -0,0 +1,31 @@
+#
+# SonarQube, open source software quality management tool.
+# Copyright (C) 2008-2014 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# SonarQube is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+#
+# SonarQube 5.1
+# SONAR-6187
+#
+class AddCharacteristicUsabilityAndSubCharacteristicsCompliance < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration 'org.sonar.server.db.migrations.v51.AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration'
+ end
+
+end
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java
index 58e6e6e747a..daa3c1d0eea 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalSettings.java
@@ -19,6 +19,8 @@
*/
package org.sonar.batch.bootstrap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
@@ -27,6 +29,8 @@ import org.sonar.batch.protocol.input.GlobalRepositories;
public class GlobalSettings extends Settings {
+ private static final Logger LOG = LoggerFactory.getLogger(GlobalSettings.class);
+
private final BootstrapProperties bootstrapProps;
private final GlobalRepositories globalReferentials;
private final DefaultAnalysisMode mode;
@@ -45,6 +49,7 @@ public class GlobalSettings extends Settings {
private void init() {
addProperties(globalReferentials.globalSettings());
addProperties(bootstrapProps.properties());
+ LOG.info("Server id: " + getString(CoreProperties.SERVER_ID));
}
@Override
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java
index 6032e60b9b6..8ab889c30d5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java
@@ -20,10 +20,6 @@
package org.sonar.batch.cpd;
import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.resources.Qualifiers;
import org.sonar.batch.cpd.decorators.DuplicationDensityDecorator;
import org.sonar.batch.cpd.decorators.SumDuplicationsDecorator;
import org.sonar.batch.cpd.index.IndexFactory;
@@ -34,38 +30,6 @@ public final class CpdComponents {
public static List all() {
return ImmutableList.of(
- PropertyDefinition.builder(CoreProperties.CPD_CROSS_PROJECT)
- .defaultValue(CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE + "")
- .name("Cross project duplication detection")
- .description("By default, SonarQube detects duplications at sub-project level. This means that a block "
- + "duplicated on two sub-projects of the same project won't be reported. Setting this parameter to \"true\" "
- + "allows to detect duplicates across sub-projects and more generally across projects. Note that activating "
- + "this property will slightly increase each SonarQube analysis time.")
- .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
- .type(PropertyType.BOOLEAN)
- .build(),
- PropertyDefinition.builder(CoreProperties.CPD_SKIP_PROPERTY)
- .defaultValue("false")
- .name("Skip")
- .description("Disable detection of duplications")
- .hidden()
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
- .type(PropertyType.BOOLEAN)
- .build(),
- PropertyDefinition.builder(CoreProperties.CPD_EXCLUSIONS)
- .defaultValue("")
- .name("Duplication Exclusions")
- .description("Patterns used to exclude some source files from the duplication detection mechanism. " +
- "See below to know how to use wildcards to specify this property.")
- .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS_EXCLUSIONS)
- .multiValues(true)
- .build(),
-
CpdSensor.class,
CpdMappings.class,
SumDuplicationsDecorator.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java
index 2205227cd31..d753fc45e1f 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java
@@ -83,6 +83,9 @@ public class JavaCpdEngine extends CpdEngine {
*/
private static final int TIMEOUT = 5 * 60;
+ private static final int MAX_CLONE_GROUP_PER_FILE = 100;
+ private static final int MAX_CLONE_PART_PER_GROUP = 100;
+
private final IndexFactory indexFactory;
private final FileSystem fs;
private final Settings settings;
@@ -230,12 +233,25 @@ public class JavaCpdEngine extends CpdEngine {
.setFromCore()
.save();
+ int cloneGroupCount = 0;
for (CloneGroup duplication : duplications) {
+ cloneGroupCount++;
+ if (cloneGroupCount > MAX_CLONE_GROUP_PER_FILE) {
+ LOG.warn("Too many duplication groups on file " + inputFile.relativePath() + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + " groups.");
+ break;
+ }
NewDuplication builder = context.newDuplication();
ClonePart originPart = duplication.getOriginPart();
builder.originBlock(inputFile, originPart.getStartLine(), originPart.getEndLine());
+ int clonePartCount = 0;
for (ClonePart part : duplication.getCloneParts()) {
if (!part.equals(originPart)) {
+ clonePartCount++;
+ if (clonePartCount > MAX_CLONE_PART_PER_GROUP) {
+ LOG.warn("Too many duplication references on file " + inputFile.relativePath() + " for block at line " + originPart.getStartLine() + ". Keep only the first "
+ + MAX_CLONE_PART_PER_GROUP + " references.");
+ break;
+ }
((DefaultDuplication) builder).isDuplicatedBy(part.getResourceId(), part.getStartLine(), part.getEndLine());
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java
index 2e3a6348466..666ec9b633f 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java
@@ -82,7 +82,7 @@ public class SqaleRatingDecorator implements Decorator {
@Override
public void decorate(Resource resource, DecoratorContext context) {
- if (ResourceUtils.isPersistable(resource) && !ResourceUtils.isUnitTestClass(resource)) {
+ if (ResourceUtils.isPersistable(resource) && !ResourceUtils.isUnitTestFile(resource)) {
Long developmentCost = getDevelopmentCost(context);
context.saveMeasure(new Measure(CoreMetrics.DEVELOPMENT_COST, Long.toString(developmentCost)));
diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java
index 8f01f2c233b..6dd1202dc42 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/DefaultDependencyValueCoder.java
@@ -22,47 +22,26 @@ package org.sonar.batch.dependency;
import com.persistit.Value;
import com.persistit.encoding.CoderContext;
import com.persistit.encoding.ValueCoder;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
-import org.sonar.batch.scan.filesystem.InputPathCache;
class DefaultDependencyValueCoder implements ValueCoder {
- private InputPathCache inputPathCache;
-
- public DefaultDependencyValueCoder(InputPathCache inputPathCache) {
- this.inputPathCache = inputPathCache;
- }
-
@Override
public void put(Value value, Object object, CoderContext context) {
DefaultDependency dep = (DefaultDependency) object;
- value.putUTF(((DefaultInputFile) dep.from()).moduleKey());
- value.putUTF(((DefaultInputFile) dep.from()).relativePath());
- value.putUTF(((DefaultInputFile) dep.to()).moduleKey());
- value.putUTF(((DefaultInputFile) dep.to()).relativePath());
+ value.putUTF(dep.fromKey());
+ value.putUTF(dep.toKey());
value.put(dep.weight());
}
@Override
public Object get(Value value, Class clazz, CoderContext context) {
- String fromModuleKey = value.getString();
- String fromRelativePath = value.getString();
- InputFile from = inputPathCache.getFile(fromModuleKey, fromRelativePath);
- if (from == null) {
- throw new IllegalStateException("Unable to load InputFile " + fromModuleKey + ":" + fromRelativePath);
- }
- String toModuleKey = value.getString();
- String toRelativePath = value.getString();
- InputFile to = inputPathCache.getFile(toModuleKey, toRelativePath);
- if (to == null) {
- throw new IllegalStateException("Unable to load InputFile " + toModuleKey + ":" + toRelativePath);
- }
+ String fromKey = value.getString();
+ String toKey = value.getString();
int weight = value.getInt();
return new DefaultDependency()
- .from(from)
- .to(to)
- .weight(weight);
+ .setFromKey(fromKey)
+ .setToKey(toKey)
+ .setWeight(weight);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java b/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java
index 9e3709a5532..50ce7577c7b 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/dependency/DependencyCache.java
@@ -21,14 +21,10 @@ package org.sonar.batch.dependency;
import com.google.common.base.Preconditions;
import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.dependency.Dependency;
import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
import org.sonar.batch.index.Cache;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.index.Caches;
-import org.sonar.batch.scan.filesystem.InputPathCache;
import javax.annotation.CheckForNull;
@@ -38,29 +34,29 @@ import javax.annotation.CheckForNull;
*/
public class DependencyCache implements BatchComponent {
- private final Cache<Dependency> cache;
+ private final Cache<DefaultDependency> cache;
- public DependencyCache(Caches caches, InputPathCache inputPathCache) {
- caches.registerValueCoder(DefaultDependency.class, new DefaultDependencyValueCoder(inputPathCache));
+ public DependencyCache(Caches caches) {
+ caches.registerValueCoder(DefaultDependency.class, new DefaultDependencyValueCoder());
cache = caches.createCache("dependencies");
}
- public Iterable<Entry<Dependency>> entries() {
+ public Iterable<Entry<DefaultDependency>> entries() {
return cache.entries();
}
@CheckForNull
- public Dependency get(String moduleKey, InputFile from, InputFile to) {
+ public DefaultDependency get(String moduleKey, String fromKey, String toKey) {
Preconditions.checkNotNull(moduleKey);
- Preconditions.checkNotNull(from);
- Preconditions.checkNotNull(to);
- return cache.get(moduleKey, ((DefaultInputFile) from).key(), ((DefaultInputFile) to).key());
+ Preconditions.checkNotNull(fromKey);
+ Preconditions.checkNotNull(toKey);
+ return cache.get(moduleKey, fromKey, toKey);
}
- public DependencyCache put(String moduleKey, Dependency dependency) {
+ public DependencyCache put(String moduleKey, DefaultDependency dependency) {
Preconditions.checkNotNull(moduleKey);
Preconditions.checkNotNull(dependency);
- cache.put(moduleKey, ((DefaultInputFile) dependency.from()).key(), ((DefaultInputFile) dependency.to()).key(), dependency);
+ cache.put(moduleKey, dependency.fromKey(), dependency.toKey(), dependency);
return this;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
index 2b309da1100..a8d96036863 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/deprecated/DeprecatedSensorContext.java
@@ -37,19 +37,15 @@ import org.sonar.api.design.Dependency;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Directory;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.ProjectLink;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.*;
import org.sonar.api.rules.Violation;
import org.sonar.api.utils.SonarException;
-import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.sensor.DefaultSensorContext;
import org.sonar.batch.sensor.coverage.CoverageExclusions;
+import javax.annotation.Nullable;
+
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
@@ -66,8 +62,8 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen
public DeprecatedSensorContext(SonarIndex index, Project project, Settings settings, FileSystem fs, ActiveRules activeRules,
AnalysisMode analysisMode, ComponentDataCache componentDataCache, CoverageExclusions coverageFilter,
- DuplicationCache duplicationCache, SensorStorage sensorStorage) {
- super(settings, fs, activeRules, analysisMode, componentDataCache, duplicationCache, sensorStorage);
+ SensorStorage sensorStorage) {
+ super(settings, fs, activeRules, analysisMode, componentDataCache, sensorStorage);
this.index = index;
this.project = project;
this.coverageFilter = coverageFilter;
@@ -255,7 +251,7 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen
}
@Override
- public Event createEvent(Resource resource, String name, String description, String category, Date date) {
+ public Event createEvent(Resource resource, String name, String description, String category, @Nullable Date date) {
return index.addEvent(resource, name, description, category, date);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java
index 3569ba9a913..0fdae1a037b 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/design/DsmDecorator.java
@@ -75,9 +75,7 @@ public abstract class DsmDecorator implements Decorator {
}
Dsm<Resource> dsm = getDsm(children, feedbackEdges);
// Optimization, don't save DSM if there is no dependency at all
- if (dsm.hasAtLeastOneDependency()) {
- saveDsm(context, dsm);
- }
+ saveDsm(context, dsm);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java b/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java
index 10d4b9d543e..a07eded789c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java
@@ -46,6 +46,10 @@ public class BatchResource {
}
}
+ public String key() {
+ return r.getEffectiveKey();
+ }
+
public int batchId() {
return batchId;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
index c98b88773e6..02efaab0f00 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
@@ -300,15 +300,7 @@ public class Cache<V> {
* Lazy-loading values for given keys
*/
public Iterable<V> values(Object firstKey, Object secondKey) {
- try {
- exchange.clear();
- exchange.append(firstKey).append(secondKey).append(Key.BEFORE);
- Exchange iteratorExchange = new Exchange(exchange);
- KeyFilter filter = new KeyFilter().append(KeyFilter.simpleTerm(firstKey)).append(KeyFilter.simpleTerm(secondKey));
- return new ValueIterable<V>(iteratorExchange, filter);
- } catch (Exception e) {
- throw failToGetValues(e);
- }
+ return new ValueIterable<V>(exchange, firstKey, secondKey);
}
private IllegalStateException failToGetValues(Exception e) {
@@ -319,41 +311,22 @@ public class Cache<V> {
* Lazy-loading values for a given key
*/
public Iterable<V> values(Object firstKey) {
- try {
- exchange.clear();
- exchange.append(firstKey).append(Key.BEFORE);
- Exchange iteratorExchange = new Exchange(exchange);
- KeyFilter filter = new KeyFilter().append(KeyFilter.simpleTerm(firstKey));
- return new ValueIterable<V>(iteratorExchange, filter);
- } catch (Exception e) {
- throw failToGetValues(e);
- }
+ return new ValueIterable<V>(exchange, firstKey);
}
/**
* Lazy-loading values
*/
public Iterable<V> values() {
- try {
- exchange.clear().append(Key.BEFORE);
- Exchange iteratorExchange = new Exchange(exchange);
- KeyFilter filter = new KeyFilter().append(KeyFilter.ALL);
- return new ValueIterable<V>(iteratorExchange, filter);
- } catch (Exception e) {
- throw failToGetValues(e);
- }
+ return new ValueIterable<V>(exchange);
}
public Iterable<Entry<V>> entries() {
- exchange.clear().to(Key.BEFORE);
- KeyFilter filter = new KeyFilter().append(KeyFilter.ALL);
- return new EntryIterable<V>(new Exchange(exchange), filter);
+ return new EntryIterable<V>(exchange);
}
public Iterable<Entry<V>> entries(Object firstKey) {
- exchange.clear().append(firstKey).append(Key.BEFORE);
- KeyFilter filter = new KeyFilter().append(KeyFilter.simpleTerm(firstKey));
- return new EntryIterable<V>(new Exchange(exchange), filter);
+ return new EntryIterable<V>(exchange, firstKey);
}
private void resetKey(Object key) {
@@ -383,15 +356,25 @@ public class Cache<V> {
//
private static class ValueIterable<T> implements Iterable<T> {
- private final Iterator<T> iterator;
+ private final Exchange originExchange;
+ private final Object[] keys;
- private ValueIterable(Exchange exchange, KeyFilter keyFilter) {
- this.iterator = new ValueIterator<T>(exchange, keyFilter);
+ private ValueIterable(Exchange originExchange, Object... keys) {
+ this.originExchange = originExchange;
+ this.keys = keys;
}
@Override
public Iterator<T> iterator() {
- return iterator;
+ originExchange.clear();
+ KeyFilter filter = new KeyFilter();
+ for (Object key : keys) {
+ originExchange.append(key);
+ filter = filter.append(KeyFilter.simpleTerm(key));
+ }
+ originExchange.append(Key.BEFORE);
+ Exchange iteratorExchange = new Exchange(originExchange);
+ return new ValueIterator<T>(iteratorExchange, filter);
}
}
@@ -434,15 +417,25 @@ public class Cache<V> {
}
private static class EntryIterable<T> implements Iterable<Entry<T>> {
- private final EntryIterator<T> it;
+ private final Exchange originExchange;
+ private final Object[] keys;
- private EntryIterable(Exchange exchange, KeyFilter keyFilter) {
- it = new EntryIterator<T>(exchange, keyFilter);
+ private EntryIterable(Exchange originExchange, Object... keys) {
+ this.originExchange = originExchange;
+ this.keys = keys;
}
@Override
public Iterator<Entry<T>> iterator() {
- return it;
+ originExchange.clear();
+ KeyFilter filter = new KeyFilter();
+ for (Object key : keys) {
+ originExchange.append(key);
+ filter = filter.append(KeyFilter.simpleTerm(key));
+ }
+ originExchange.append(Key.BEFORE);
+ Exchange iteratorExchange = new Exchange(originExchange);
+ return new EntryIterator<T>(iteratorExchange, filter);
}
}
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 4cc275dc4aa..0e2c128b185 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
@@ -38,14 +38,7 @@ import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.MeasuresFilters;
-import org.sonar.api.resources.Directory;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.ProjectLink;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.ResourceUtils;
-import org.sonar.api.resources.Scopes;
+import org.sonar.api.resources.*;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.Violation;
import org.sonar.api.scan.filesystem.PathResolver;
@@ -59,16 +52,7 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
public class DefaultIndex extends SonarIndex {
@@ -103,7 +87,11 @@ public class DefaultIndex extends SonarIndex {
private final ResourceCache resourceCache;
private final MetricFinder metricFinder;
-
+ private final MeasureCache measureCache;
+ private final ResourceKeyMigration migration;
+ private final DependencyPersister dependencyPersister;
+ private final LinkPersister linkPersister;
+ private final EventPersister eventPersister;
// caches
private Project currentProject;
private Map<Resource, Bucket> buckets = Maps.newLinkedHashMap();
@@ -112,11 +100,6 @@ public class DefaultIndex extends SonarIndex {
private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = Maps.newLinkedHashMap();
private ProjectTree projectTree;
private ModuleIssues moduleIssues;
- private final MeasureCache measureCache;
- private final ResourceKeyMigration migration;
- private final DependencyPersister dependencyPersister;
- private final LinkPersister linkPersister;
- private final EventPersister eventPersister;
public DefaultIndex(ResourceCache resourceCache, DependencyPersister dependencyPersister,
LinkPersister linkPersister, EventPersister eventPersister, ProjectTree projectTree, MetricFinder metricFinder,
@@ -131,9 +114,9 @@ public class DefaultIndex extends SonarIndex {
this.measureCache = measureCache;
}
- public DefaultIndex(ResourceCache resourceCache, ProjectTree projectTree, MetricFinder metricFinder, MeasureCache measureCache) {
+ public DefaultIndex(ResourceCache resourceCache, DependencyPersister dependencyPersister, ProjectTree projectTree, MetricFinder metricFinder, MeasureCache measureCache) {
this.resourceCache = resourceCache;
- this.dependencyPersister = null;
+ this.dependencyPersister = dependencyPersister;
this.linkPersister = null;
this.eventPersister = null;
this.projectTree = projectTree;
@@ -205,6 +188,12 @@ public class DefaultIndex extends SonarIndex {
}
}
+ // store dependencies
+ for (Dependency dep : dependencies) {
+ dependencyPersister.saveDependency(currentProject, dep);
+ }
+
+ // Keep only inter module dependencies
Set<Dependency> projectDependencies = getDependenciesBetweenProjects();
dependencies.clear();
incomingDependenciesByResource.clear();
@@ -285,8 +274,10 @@ public class DefaultIndex extends SonarIndex {
// Reload resources
Resource from = getResource(dependency.getFrom());
Preconditions.checkArgument(from != null, dependency.getFrom() + " is not indexed");
+ dependency.setFrom(from);
Resource to = getResource(dependency.getTo());
Preconditions.checkArgument(to != null, dependency.getTo() + " is not indexed");
+ dependency.setTo(to);
Dependency existingDep = getEdge(from, to);
if (existingDep != null) {
@@ -297,10 +288,7 @@ public class DefaultIndex extends SonarIndex {
if (parentDependency != null) {
addDependency(parentDependency);
}
-
- if (registerDependency(dependency) && dependencyPersister != null) {
- dependencyPersister.saveDependency(currentProject, from, to, dependency, parentDependency);
- }
+ registerDependency(dependency);
return dependency;
}
@@ -476,10 +464,11 @@ public class DefaultIndex extends SonarIndex {
}
@Override
- public Event addEvent(Resource resource, String name, String description, String category, Date date) {
+ public Event addEvent(Resource resource, String name, String description, String category, @Nullable Date date) {
Event event = new Event(name, description, category);
- event.setDate(date);
- event.setCreatedAt(new Date());
+ if (date != null) {
+ event.setDate(date);
+ }
if (eventPersister != null) {
eventPersister.saveEvent(resource, event);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java
index 15886b6b4ac..13c73e278f5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java
@@ -19,27 +19,46 @@
*/
package org.sonar.batch.index;
+import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.design.Dependency;
import org.sonar.api.design.DependencyDto;
import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
+import org.sonar.batch.dependency.DependencyCache;
+
+import javax.annotation.Nullable;
public final class DependencyPersister {
- private ResourceCache resourceCache;
- private DatabaseSession session;
+ private final ResourceCache resourceCache;
+ private final DatabaseSession session;
+ private final DependencyCache dependencyCache;
- public DependencyPersister(ResourceCache resourceCache, DatabaseSession session) {
+ public DependencyPersister(ResourceCache resourceCache, DependencyCache dependencyCache, @Nullable DatabaseSession session) {
this.resourceCache = resourceCache;
+ this.dependencyCache = dependencyCache;
this.session = session;
}
- public void saveDependency(Project project, Resource from, Resource to, Dependency dependency, Dependency parentDependency) {
- BatchResource fromResource = resourceCache.get(from);
- BatchResource toResource = resourceCache.get(to);
+ public DependencyPersister(ResourceCache resourceCache, DependencyCache dependencyCache) {
+ this(resourceCache, dependencyCache, null);
+ }
+
+ public void saveDependency(Project project, Dependency dependency) {
+ BatchResource fromResource = resourceCache.get(dependency.getFrom());
+ BatchResource toResource = resourceCache.get(dependency.getTo());
BatchResource projectResource = resourceCache.get(project);
+ if (fromResource.isFile() && toResource.isFile()) {
+ dependencyCache.put(project.getEffectiveKey(), new DefaultDependency().setFromKey(fromResource.key()).setToKey(toResource.key()).setWeight(dependency.getWeight()));
+ }
+
+ if (session != null) {
+ saveInDB(project, dependency, fromResource, toResource, projectResource);
+ }
+ }
+
+ private void saveInDB(Project project, Dependency dependency, BatchResource fromResource, BatchResource toResource, BatchResource projectResource) {
DependencyDto model = new DependencyDto();
model.setProjectSnapshotId(projectResource.snapshotId());
model.setUsage(dependency.getUsage());
@@ -53,8 +72,11 @@ public final class DependencyPersister {
model.setToScope(toResource.resource().getScope());
model.setToSnapshotId(toResource.snapshotId());
+ Dependency parentDependency = dependency.getParent();
if (parentDependency != null) {
- // assume that it has been previously saved
+ if (parentDependency.getId() == null) {
+ saveDependency(project, parentDependency);
+ }
model.setParentDependencyId(parentDependency.getId());
}
session.save(model);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java
index b01efa10063..8ee521c247e 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java
@@ -22,16 +22,22 @@ package org.sonar.batch.index;
import org.sonar.api.batch.Event;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.System2;
+import java.util.Date;
import java.util.List;
+import static com.google.common.base.Preconditions.checkState;
+
public class EventPersister {
+ private final System2 system2;
private DatabaseSession session;
private ResourceCache resourceCache;
- public EventPersister(DatabaseSession session, ResourceCache resourceCache) {
+ public EventPersister(DatabaseSession session, ResourceCache resourceCache, System2 system2) {
this.session = session;
this.resourceCache = resourceCache;
+ this.system2 = system2;
}
public List<Event> getEvents(Resource resource) {
@@ -45,16 +51,16 @@ public class EventPersister {
public void saveEvent(Resource resource, Event event) {
BatchResource batchResource = resourceCache.get(resource.getEffectiveKey());
- if (batchResource == null) {
- throw new IllegalStateException("Unknow component: " + resource);
- }
+ checkState(batchResource != null, "Unknown component: " + resource);
+
+ event.setCreatedAt(new Date(system2.now()));
if (event.getDate() == null) {
event.setSnapshot(batchResource.snapshot());
} else {
event.setResourceId(batchResource.resource().getId());
}
+
session.save(event);
session.commit();
-
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java
index 0066e330959..66d5623c04b 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java
@@ -150,7 +150,7 @@ public class ResourcePersister implements ScanPersister {
snapshot = session.save(snapshot);
session.commit();
- if (!permissions.hasRoles(project)) {
+ if (parent == null && !permissions.hasRoles(project)) {
permissions.grantDefaultRoles(project);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
index 8013dd54d9d..245f85bd38d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.dependency.Dependency;
+import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
import org.sonar.api.batch.sensor.duplication.Duplication;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
@@ -92,7 +92,7 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
storeDuplication(container);
// storeTestCases(container);
// storeCoveragePerTest(container);
- // storeDependencies(container);
+ storeDependencies(container);
}
@@ -174,7 +174,7 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
private void storeDependencies(ProjectScanContainer container) {
DependencyCache dependencyCache = container.getComponentByType(DependencyCache.class);
- for (Entry<Dependency> entry : dependencyCache.entries()) {
+ for (Entry<DefaultDependency> entry : dependencyCache.entries()) {
String fromKey = entry.key()[1].toString();
String toKey = entry.key()[2].toString();
if (!dependencies.containsKey(fromKey)) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java b/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java
index 30956a0a263..137b1419d9c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/profiling/PhasesSumUpTimeProfiler.java
@@ -43,6 +43,7 @@ import org.sonar.batch.events.BatchStepHandler;
import org.sonar.batch.phases.Phases;
import org.sonar.batch.phases.event.PersisterExecutionHandler;
import org.sonar.batch.phases.event.PersistersPhaseHandler;
+import org.sonar.batch.util.BatchUtils;
import javax.annotation.Nullable;
@@ -118,7 +119,7 @@ public class PhasesSumUpTimeProfiler implements ProjectAnalysisHandler, SensorEx
println(" -------- End of profiling of module " + module.getName() + " --------");
println("");
String fileName = module.getKey() + "-profiler.properties";
- dumpToFile(props, fileName);
+ dumpToFile(props, BatchUtils.cleanKeyForFilename(fileName));
totalProfiling.merge(currentModuleProfiling);
if (module.isRoot() && !module.getModules().isEmpty()) {
dumpTotalExecutionSummary();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
index eadf7155dc1..e3ef6349a51 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
@@ -33,6 +33,7 @@ import org.sonar.api.CoreProperties;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.batch.bootstrap.TaskProperties;
+import org.sonar.batch.util.BatchUtils;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -189,9 +190,7 @@ public class ProjectReactorBuilder {
protected File initModuleWorkDir(File moduleBaseDir, Map<String, String> moduleProperties) {
String workDir = moduleProperties.get(CoreProperties.WORKING_DIRECTORY);
if (StringUtils.isBlank(workDir)) {
- String cleanKey = StringUtils.deleteWhitespace(moduleProperties.get(CoreProperties.PROJECT_KEY_PROPERTY));
- cleanKey = StringUtils.replace(cleanKey, ":", "_");
- return new File(rootProjectWorkDir, cleanKey);
+ return new File(rootProjectWorkDir, BatchUtils.cleanKeyForFilename(moduleProperties.get(CoreProperties.PROJECT_KEY_PROPERTY)));
}
File customWorkDir = new File(workDir);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java
index d92dc698bea..88970aa75da 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java
@@ -123,7 +123,7 @@ public class ProjectReactorValidator {
private void validateBranch(List<String> validationMessages, @Nullable String branch) {
if (StringUtils.isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) {
validationMessages.add(String.format("\"%s\" is not a valid branch name. "
- + "Allowed characters are alphanumeric, '-', '_' and '.'.", branch));
+ + "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branch));
}
}
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 850b6e984bf..a2f46a03fe4 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
@@ -41,6 +41,7 @@ import org.sonar.batch.bootstrap.ExtensionUtils;
import org.sonar.batch.bootstrap.MetricProvider;
import org.sonar.batch.debt.DebtModelProvider;
import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.dependency.DependencyCache;
import org.sonar.batch.deprecated.components.DefaultResourceCreationLock;
import org.sonar.batch.deprecated.components.PeriodsDefinition;
import org.sonar.batch.duplication.DuplicationCache;
@@ -189,6 +190,10 @@ public class ProjectScanContainer extends ComponentContainer {
// Duplications
DuplicationCache.class,
+ // Dependencies
+ DependencyPersister.class,
+ DependencyCache.class,
+
ProjectSettings.class,
ScanTaskObservers.class);
@@ -196,7 +201,6 @@ public class ProjectScanContainer extends ComponentContainer {
private void addDataBaseComponents() {
add(
- DependencyPersister.class,
EventPersister.class,
LinkPersister.class,
MeasurePersister.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java
index e3ff5fe20e9..16441588ac9 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFilters.java
@@ -53,6 +53,10 @@ public class ExclusionFilters implements BatchComponent {
log("Excluded tests: ", testExclusions);
}
+ public boolean hasPattern() {
+ return mainInclusions.length > 0 || mainExclusions.length > 0 || testInclusions.length > 0 || testExclusions.length > 0;
+ }
+
private void log(String title, PathPattern[] patterns) {
if (patterns.length > 0) {
LOG.info(title);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
index 47acad7208c..c5eb6bef9b4 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java
@@ -23,6 +23,8 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputFile;
@@ -52,6 +54,8 @@ import java.util.concurrent.TimeUnit;
*/
public class FileIndexer implements BatchComponent {
+ private static final Logger LOG = LoggerFactory.getLogger(FileIndexer.class);
+
private static final IOFileFilter DIR_FILTER = FileFilterUtils.and(HiddenFileFilter.VISIBLE, FileFilterUtils.notFileFilter(FileFilterUtils.prefixFileFilter(".")));
private static final IOFileFilter FILE_FILTER = HiddenFileFilter.VISIBLE;
@@ -94,6 +98,10 @@ public class FileIndexer implements BatchComponent {
waitForTasksToComplete();
progressReport.stop(progress.count() + " files indexed");
+
+ if (exclusionFilters.hasPattern()) {
+ LOG.info(progress.excludedByPatternsCount() + " files ignored because of inclusion/exclusion patterns");
+ }
}
private void waitForTasksToComplete() {
@@ -134,6 +142,8 @@ public class FileIndexer implements BatchComponent {
inputFile.setModuleBaseDir(fileSystem.baseDirPath());
if (exclusionFilters.accept(inputFile, type)) {
indexFile(inputFileBuilder, fileSystem, progress, inputFile, type);
+ } else {
+ progress.increaseExcludedByPatternsCount();
}
}
}
@@ -174,6 +184,7 @@ public class FileIndexer implements BatchComponent {
private class Progress {
private final Set<Path> indexed = new HashSet<>();
+ private int excludedByPatternsCount = 0;
synchronized void markAsIndexed(InputFile inputFile) {
if (indexed.contains(inputFile.path())) {
@@ -184,6 +195,14 @@ public class FileIndexer implements BatchComponent {
progressReport.message(indexed.size() + " files indexed... (last one was " + inputFile.relativePath() + ")");
}
+ void increaseExcludedByPatternsCount() {
+ excludedByPatternsCount++;
+ }
+
+ public int excludedByPatternsCount() {
+ return excludedByPatternsCount;
+ }
+
int count() {
return indexed.size();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java
index 654134b0519..7dd0c1eab1c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java
@@ -26,7 +26,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorStorage;
-import org.sonar.api.batch.sensor.dependency.Dependency;
+import org.sonar.api.batch.sensor.dependency.NewDependency;
import org.sonar.api.batch.sensor.dependency.internal.DefaultDependency;
import org.sonar.api.batch.sensor.duplication.NewDuplication;
import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
@@ -43,7 +43,6 @@ import org.sonar.api.batch.sensor.test.internal.DefaultCoverage;
import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseCoverage;
import org.sonar.api.batch.sensor.test.internal.DefaultTestCaseExecution;
import org.sonar.api.config.Settings;
-import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
@@ -56,18 +55,16 @@ public class DefaultSensorContext implements SensorContext {
private final FileSystem fs;
private final ActiveRules activeRules;
private final ComponentDataCache componentDataCache;
- private final DuplicationCache duplicationCache;
private final SensorStorage sensorStorage;
private final AnalysisMode analysisMode;
public DefaultSensorContext(Settings settings, FileSystem fs, ActiveRules activeRules, AnalysisMode analysisMode, ComponentDataCache componentDataCache,
- DuplicationCache duplicationCache, SensorStorage sensorStorage) {
+ SensorStorage sensorStorage) {
this.settings = settings;
this.fs = fs;
this.activeRules = activeRules;
this.analysisMode = analysisMode;
this.componentDataCache = componentDataCache;
- this.duplicationCache = duplicationCache;
this.sensorStorage = sensorStorage;
}
@@ -132,7 +129,7 @@ public class DefaultSensorContext implements SensorContext {
}
@Override
- public Dependency newDependency() {
+ public NewDependency newDependency() {
return new DefaultDependency(sensorStorage);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
index 300fe651380..d512c02ca96 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
@@ -58,6 +58,7 @@ import org.sonar.api.test.Testable;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.sensor.coverage.CoverageExclusions;
import org.sonar.core.component.ComponentKeys;
@@ -70,17 +71,20 @@ public class DefaultSensorStorage implements SensorStorage {
private final DefaultIndex sonarIndex;
private final CoverageExclusions coverageExclusions;
private final DuplicationCache duplicationCache;
+ private final ResourceCache resourceCache;
public DefaultSensorStorage(MetricFinder metricFinder, Project project,
ResourcePerspectives perspectives,
Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache,
- DuplicationCache duplicationCache, DefaultIndex sonarIndex, CoverageExclusions coverageExclusions) {
+ DuplicationCache duplicationCache, DefaultIndex sonarIndex, CoverageExclusions coverageExclusions,
+ ResourceCache resourceCache) {
this.metricFinder = metricFinder;
this.project = project;
this.perspectives = perspectives;
this.sonarIndex = sonarIndex;
this.coverageExclusions = coverageExclusions;
this.duplicationCache = duplicationCache;
+ this.resourceCache = resourceCache;
}
private Metric findMetricOrFail(String metricKey) {
@@ -241,10 +245,10 @@ public class DefaultSensorStorage implements SensorStorage {
@Override
public void store(org.sonar.api.batch.sensor.dependency.Dependency dep) {
- File fromResource = getFile(dep.from());
- File toResource = getFile(dep.to());
+ File fromResource = (File) resourceCache.get(dep.fromKey()).resource();
+ File toResource = (File) resourceCache.get(dep.toKey()).resource();
if (sonarIndex.getEdge(fromResource, toResource) != null) {
- throw new IllegalStateException("Dependency between " + dep.from() + " and " + dep.to() + " was already saved.");
+ throw new IllegalStateException("Dependency between " + dep.fromKey() + " and " + dep.toKey() + " was already saved.");
}
Directory fromParent = fromResource.getParent();
Directory toParent = toResource.getParent();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java b/sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java
new file mode 100644
index 00000000000..e6321ac8b3b
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/util/BatchUtils.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.util;
+
+import org.apache.commons.lang.StringUtils;
+
+public class BatchUtils {
+
+ private BatchUtils() {
+ }
+
+ /**
+ * Clean provided string to remove chars that are not valid as file name.
+ * @param projectKey e.g. my:file
+ */
+ public static String cleanKeyForFilename(String projectKey) {
+ String cleanKey = StringUtils.deleteWhitespace(projectKey);
+ return StringUtils.replace(cleanKey, ":", "_");
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java b/sonar-batch/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java
index 90632426ee3..1b5f9310b0a 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/cpd/CpdComponentsTest.java
@@ -27,6 +27,6 @@ public class CpdComponentsTest {
@Test
public void getExtensions() {
- assertThat(CpdComponents.all()).hasSize(10);
+ assertThat(CpdComponents.all().size()).isGreaterThan(0);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java
index fa3ceecc3a5..7cc8ca87bc1 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/DirectoryDsmDecoratorTest.java
@@ -97,12 +97,16 @@ public class DirectoryDsmDecoratorTest {
public void testDirectoryDsmDecoratorNoDependency() {
decorator.decorate(dir, dirContext);
- verify(dirContext, times(4)).saveMeasure(any(Measure.class));
+ verify(dirContext, times(5)).saveMeasure(any(Measure.class));
verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_CYCLES, 0.0));
verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_FEEDBACK_EDGES, 0.0));
verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_TANGLES, 0.0));
verify(dirContext).saveMeasure(isMeasureWithValue(CoreMetrics.FILE_EDGES_WEIGHT, 0.0));
+ verify(dirContext).saveMeasure(
+ isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+ "[{\"i\":1,\"n\":\"Foo1.java\",\"q\":\"FIL\",\"v\":[{},{}]},{\"i\":2,\"n\":\"Foo2.java\",\"q\":\"FIL\",\"v\":[{},{}]}]"));
+
}
@Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java
index c18da1fdde9..0d78693fa59 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/ProjectDsmDecoratorTest.java
@@ -19,7 +19,6 @@
*/
package org.sonar.batch.design;
-import edu.emory.mathcs.backport.java.util.Collections;
import org.apache.commons.lang.ObjectUtils;
import org.junit.Before;
import org.junit.Test;
@@ -87,17 +86,6 @@ public class ProjectDsmDecoratorTest {
Project child = new Project("child").setParent(p);
decorator.decorate(p, rootContext);
- // Should not do anything if module has no dir
- when(rootContext.getChildren()).thenReturn(Collections.emptyList());
- decorator.decorate(root, rootContext);
-
- verify(rootContext, never()).saveMeasure(any(Measure.class));
- }
-
- @Test
- public void testProjectDsmDecoratorNoDependency() {
- decorator.decorate(root, rootContext);
-
verify(rootContext, never()).saveMeasure(any(Measure.class));
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java
index a9a41264adf..bc65050aab2 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/design/SubProjectDsmDecoratorTest.java
@@ -96,12 +96,14 @@ public class SubProjectDsmDecoratorTest {
public void testSubProjectDsmDecoratorNoDependency() {
decorator.decorate(module, moduleContext);
- verify(moduleContext, times(4)).saveMeasure(any(Measure.class));
+ verify(moduleContext, times(5)).saveMeasure(any(Measure.class));
verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_CYCLES, 0.0));
verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_FEEDBACK_EDGES, 0.0));
verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_TANGLES, 0.0));
verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DIRECTORY_EDGES_WEIGHT, 0.0));
+ verify(moduleContext).saveMeasure(isMeasureWithValue(CoreMetrics.DEPENDENCY_MATRIX,
+ "[{\"i\":1,\"n\":\"src/foo1\",\"q\":\"DIR\",\"v\":[{},{}]},{\"i\":2,\"n\":\"src/foo2\",\"q\":\"DIR\",\"v\":[{},{}]}]"));
}
@Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/CacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/CacheTest.java
index 9bae728f375..0235af3dc89 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/index/CacheTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/CacheTest.java
@@ -25,6 +25,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.index.Cache.Entry;
import static org.assertj.core.api.Assertions.assertThat;
@@ -57,11 +58,15 @@ public class CacheTest {
assertThat(cache.get("france")).isEqualTo("paris");
assertThat(cache.keySet()).containsOnly("france", "italy");
assertThat(cache.keySet("france")).isEmpty();
- assertThat(cache.values()).containsOnly("paris", "rome");
+ Iterable<String> values = cache.values();
+ assertThat(values).containsOnly("paris", "rome");
+ assertThat(values).containsOnly("paris", "rome");
assertThat(cache.containsKey("france")).isTrue();
- Cache.Entry[] entries = Iterables.toArray(cache.entries(), Cache.Entry.class);
+ Iterable<Entry<String>> iterable = cache.entries();
+ Cache.Entry[] entries = Iterables.toArray(iterable, Cache.Entry.class);
assertThat(entries).hasSize(2);
+ assertThat(iterable).hasSize(2);
assertThat(entries[0].key()[0]).isEqualTo("france");
assertThat(entries[0].value()).isEqualTo("paris");
assertThat(entries[1].key()[0]).isEqualTo("italy");
@@ -74,10 +79,10 @@ public class CacheTest {
assertThat(cache.keySet("france")).isEmpty();
assertThat(cache.containsKey("france")).isFalse();
assertThat(cache.containsKey("italy")).isTrue();
- assertThat(cache.values()).containsOnly("rome");
+ assertThat(values).containsOnly("rome");
cache.clear();
- assertThat(cache.values()).isEmpty();
+ assertThat(values).isEmpty();
}
@Test
@@ -114,8 +119,10 @@ public class CacheTest {
assertThat(cache.values("europe")).containsOnly("paris", "rome");
assertThat(cache.values("oceania")).isEmpty();
- Cache.Entry[] allEntries = Iterables.toArray(cache.entries(), Cache.Entry.class);
+ Iterable<Entry<String>> iterable = cache.entries();
+ Cache.Entry[] allEntries = Iterables.toArray(iterable, Cache.Entry.class);
assertThat(allEntries).hasSize(3);
+ assertThat(iterable).hasSize(3);
assertThat(allEntries[0].key()).isEqualTo(new String[] {"asia", "china"});
assertThat(allEntries[0].value()).isEqualTo("pekin");
assertThat(allEntries[1].key()).isEqualTo(new String[] {"europe", "france"});
@@ -123,8 +130,10 @@ public class CacheTest {
assertThat(allEntries[2].key()).isEqualTo(new String[] {"europe", "italy"});
assertThat(allEntries[2].value()).isEqualTo("rome");
- Cache.Entry[] subEntries = Iterables.toArray(cache.entries("europe"), Cache.Entry.class);
+ Iterable<Entry<String>> iterable2 = cache.entries("europe");
+ Cache.Entry[] subEntries = Iterables.toArray(iterable2, Cache.Entry.class);
assertThat(subEntries).hasSize(2);
+ assertThat(iterable2).hasSize(2);
assertThat(subEntries[0].key()).isEqualTo(new String[] {"europe", "france"});
assertThat(subEntries[0].value()).isEqualTo("paris");
assertThat(subEntries[1].key()).isEqualTo(new String[] {"europe", "italy"});
@@ -171,8 +180,10 @@ public class CacheTest {
assertThat(cache.values("europe")).containsOnly("eiffel tower", "lake", "colosseum", "notre dame");
assertThat(cache.values("europe", "france")).containsOnly("eiffel tower", "lake", "notre dame");
- Cache.Entry[] allEntries = Iterables.toArray(cache.entries(), Cache.Entry.class);
+ Iterable<Entry<String>> iterable = cache.entries();
+ Cache.Entry[] allEntries = Iterables.toArray(iterable, Cache.Entry.class);
assertThat(allEntries).hasSize(7);
+ assertThat(iterable).hasSize(7);
assertThat(allEntries[0].key()).isEqualTo(new String[] {"america", "us", "new york"});
assertThat(allEntries[0].value()).isEqualTo("empire state building");
assertThat(allEntries[1].key()).isEqualTo(new String[] {"asia", "china", "pekin"});
@@ -186,8 +197,10 @@ public class CacheTest {
assertThat(allEntries[5].key()).isEqualTo(new String[] {"europe", "italy", "rome"});
assertThat(allEntries[5].value()).isEqualTo("colosseum");
- Cache.Entry[] subEntries = Iterables.toArray(cache.entries("europe"), Cache.Entry.class);
+ Iterable<Entry<String>> iterable2 = cache.entries("europe");
+ Cache.Entry[] subEntries = Iterables.toArray(iterable2, Cache.Entry.class);
assertThat(subEntries).hasSize(4);
+ assertThat(iterable2).hasSize(4);
assertThat(subEntries[0].key()).isEqualTo(new String[] {"europe", "france", "annecy"});
assertThat(subEntries[0].value()).isEqualTo("lake");
assertThat(subEntries[1].key()).isEqualTo(new String[] {"europe", "france", "paris"});
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ResourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ResourcePersisterTest.java
index 828b660613f..fee743895f0 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/index/ResourcePersisterTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/ResourcePersisterTest.java
@@ -384,6 +384,19 @@ public class ResourcePersisterTest extends AbstractDbUnitTestCase {
}
@Test
+ public void shouldNotGrantDefaultPermissionsOnModules() {
+ setupData("shared");
+ resourceCache.add(multiModuleProject, null).setSnapshot(persister.persist(null, multiModuleProject, null));
+ resourceCache.add(moduleA, multiModuleProject).setSnapshot(persister.persist(null, moduleA, multiModuleProject));
+ when(permissions.hasRoles(multiModuleProject)).thenReturn(true);
+ persister.persist(null, multiModuleProject, null);
+
+ persister.persist(null, moduleA, multiModuleProject);
+
+ verify(permissions, never()).grantDefaultRoles(moduleA);
+ }
+
+ @Test
public void shouldNotGrantDefaultPermissionsIfExistingProject() {
setupData("shared");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
index 47ebaa81f8a..cc940ae1361 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
@@ -30,6 +30,9 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.duplication.Duplication;
+import org.sonar.api.batch.sensor.measure.Measure;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.TaskResult;
import org.sonar.xoo.XooPlugin;
@@ -129,6 +132,44 @@ public class CpdMediumTest {
assertThat(cloneGroupFile2.duplicates().get(0).resourceKey()).isEqualTo(((DefaultInputFile) inputFile1).key());
}
+ // SONAR-6000
+ @Test
+ public void truncateDuplication() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ String duplicatedStuff = "Sample xoo\n";
+
+ int blockCount = 10000;
+ File xooFile1 = new File(srcDir, "sample.xoo");
+ for (int i = 0; i < blockCount; i++) {
+ FileUtils.write(xooFile1, duplicatedStuff, true);
+ FileUtils.write(xooFile1, "" + i + "\n", true);
+ }
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.minimumTokens", "1")
+ .put("sonar.cpd.xoo.minimumLines", "1")
+ .build())
+ .start();
+
+ Measure duplicatedBlocks = null;
+ for (Measure m : result.measures()) {
+ if (m.metric().key().equals("duplicated_blocks")) {
+ duplicatedBlocks = m;
+ }
+ }
+ assertThat(duplicatedBlocks.value()).isEqualTo(blockCount);
+
+ List<Duplication> duplicationGroups = result.duplicationsFor(result.inputFile("src/sample.xoo"));
+ assertThat(duplicationGroups).hasSize(1);
+
+ Duplication cloneGroup = duplicationGroups.get(0);
+ assertThat(cloneGroup.duplicates()).hasSize(100);
+ }
+
@Test
public void testIntraFileDuplications() throws IOException {
File srcDir = new File(baseDir, "src");
@@ -164,10 +205,10 @@ public class CpdMediumTest {
assertThat(cloneGroup.duplicates().get(0).startLine()).isEqualTo(5);
assertThat(cloneGroup.duplicates().get(0).length()).isEqualTo(2);
- // assertThat(result.measures()).contains(new DefaultMeasure<String>()
- // .forMetric(CoreMetrics.DUPLICATION_LINES_DATA)
- // .onFile(inputFile)
- // .withValue("1=1;2=1;3=0;4=0;5=1;6=1;7=0"));
+ assertThat(result.measures()).contains(new DefaultMeasure<String>()
+ .forMetric(CoreMetrics.DUPLICATION_LINES_DATA)
+ .onFile(inputFile)
+ .withValue("1=1;2=1;3=0;4=0;5=1;6=1;7=0"));
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java
index 4d562d4de87..e7a08a4a37c 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/dependency/DependencyMediumTest.java
@@ -34,6 +34,8 @@ import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class DependencyMediumTest {
@Rule
@@ -85,8 +87,8 @@ public class DependencyMediumTest {
.build())
.start();
- // assertThat(result.dependencyWeight(result.inputFile("src/sample.xoo"), result.inputFile("src/sample2.xoo"))).isEqualTo(3);
- // assertThat(result.dependencyWeight(result.inputFile("src/sample.xoo"), result.inputFile("src/foo/sample3.xoo"))).isEqualTo(6);
+ assertThat(result.dependencyWeight(result.inputFile("src/sample.xoo"), result.inputFile("src/sample2.xoo"))).isEqualTo(3);
+ assertThat(result.dependencyWeight(result.inputFile("src/sample.xoo"), result.inputFile("src/foo/sample3.xoo"))).isEqualTo(6);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
index 03c40181906..72023e2e180 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
@@ -173,6 +173,40 @@ public class FileSystemMediumTest {
}
@Test
+ public void fileInclusionsExclusions() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ File xooFile = new File(srcDir, "sample.xoo");
+ FileUtils.write(xooFile, "Sample xoo\ncontent");
+
+ File xooFile2 = new File(baseDir, "another.xoo");
+ FileUtils.write(xooFile2, "Sample xoo 2\ncontent");
+
+ File testDir = new File(baseDir, "test");
+ testDir.mkdir();
+
+ File xooTestFile = new File(baseDir, "sampleTest2.xoo");
+ FileUtils.write(xooTestFile, "Sample test xoo\ncontent");
+
+ File xooTestFile2 = new File(testDir, "sampleTest.xoo");
+ FileUtils.write(xooTestFile2, "Sample test xoo 2\ncontent");
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src,another.xoo")
+ .put("sonar.tests", "test,sampleTest2.xoo")
+ .put("sonar.inclusions", "src/**")
+ .put("sonar.exclusions", "**/another.*")
+ .put("sonar.test.inclusions", "**/sampleTest*.*")
+ .put("sonar.test.exclusions", "**/sampleTest2.xoo")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(2);
+ }
+
+ @Test
public void failForDuplicateInputFile() throws IOException {
File srcDir = new File(baseDir, "src");
srcDir.mkdir();
diff --git a/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java b/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java
index 2802d2f7058..e04c6843313 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/profiling/PhasesSumUpTimeProfilerTest.java
@@ -81,7 +81,7 @@ public class PhasesSumUpTimeProfilerTest {
@Test
public void testSimpleProject() throws InterruptedException {
- final Project project = mockProject("project", true);
+ final Project project = mockProject("my:project", true);
when(project.getModules()).thenReturn(Collections.<Project>emptyList());
fakeAnalysis(profiler, project);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java
index 4f7fa7507ac..19b12f7b972 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java
@@ -32,7 +32,6 @@ import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.sensor.SensorStorage;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.ComponentDataCache;
import static org.assertj.core.api.Assertions.assertThat;
@@ -65,7 +64,7 @@ public class DefaultSensorContextTest {
ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
sensorStorage = mock(SensorStorage.class);
analysisMode = mock(AnalysisMode.class);
- adaptor = new DefaultSensorContext(settings, fs, activeRules, analysisMode, componentDataCache, mock(DuplicationCache.class), sensorStorage);
+ adaptor = new DefaultSensorContext(settings, fs, activeRules, analysisMode, componentDataCache, sensorStorage);
}
@Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
index a4ed0b08571..02b526f0375 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
@@ -54,6 +54,7 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.sensor.coverage.CoverageExclusions;
import static org.assertj.core.api.Assertions.assertThat;
@@ -80,6 +81,8 @@ public class DefaultSensorStorageTest {
private Project project;
private DefaultIndex sonarIndex;
+ private ResourceCache resourceCache;
+
@Before
public void prepare() throws Exception {
activeRules = new ActiveRulesBuilder().build();
@@ -94,8 +97,9 @@ public class DefaultSensorStorageTest {
sonarIndex = mock(DefaultIndex.class);
CoverageExclusions coverageExclusions = mock(CoverageExclusions.class);
when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true);
+ resourceCache = new ResourceCache();
sensorStorage = new DefaultSensorStorage(metricFinder, project,
- resourcePerspectives, settings, fs, activeRules, componentDataCache, mock(DuplicationCache.class), sonarIndex, coverageExclusions);
+ resourcePerspectives, settings, fs, activeRules, componentDataCache, mock(DuplicationCache.class), sonarIndex, coverageExclusions, resourceCache);
}
@Test
@@ -248,10 +252,10 @@ public class DefaultSensorStorageTest {
@Test
public void shouldStoreDependencyInSameFolder() {
- File foo = File.create("src/Foo.java");
- File bar = File.create("src/Bar.java");
- when(sonarIndex.getResource(foo)).thenReturn(foo);
- when(sonarIndex.getResource(bar)).thenReturn(bar);
+ Resource foo = File.create("src/Foo.java").setEffectiveKey("foo:src/Foo.java");
+ Resource bar = File.create("src/Bar.java").setEffectiveKey("foo:src/Bar.java");
+ resourceCache.add(foo, null);
+ resourceCache.add(bar, null);
sensorStorage.store(new DefaultDependency()
.from(new DefaultInputFile("foo", "src/Foo.java").setType(Type.MAIN))
@@ -270,14 +274,15 @@ public class DefaultSensorStorageTest {
@Test
public void throw_if_attempt_to_save_same_dep_twice() {
- File foo = File.create("src/Foo.java");
- File bar = File.create("src/Bar.java");
- when(sonarIndex.getResource(foo)).thenReturn(foo);
- when(sonarIndex.getResource(bar)).thenReturn(bar);
+ Resource foo = File.create("src/Foo.java").setEffectiveKey("foo:src/Foo.java");
+ Resource bar = File.create("src/Bar.java").setEffectiveKey("foo:src/Bar.java");
+ resourceCache.add(foo, null);
+ resourceCache.add(bar, null);
+
when(sonarIndex.getEdge(foo, bar)).thenReturn(new Dependency(foo, bar));
thrown.expect(IllegalStateException.class);
- thrown.expectMessage("Dependency between [moduleKey=foo, relative=src/Foo.java, basedir=null] and [moduleKey=foo, relative=src/Bar.java, basedir=null] was already saved.");
+ thrown.expectMessage("Dependency between foo:src/Foo.java and foo:src/Bar.java was already saved.");
sensorStorage.store(new DefaultDependency()
.from(new DefaultInputFile("foo", "src/Foo.java").setType(Type.MAIN))
@@ -288,10 +293,10 @@ public class DefaultSensorStorageTest {
@Test
public void shouldStoreDependencyInDifferentFolder() {
- File foo = File.create("src1/Foo.java");
- File bar = File.create("src2/Bar.java");
- when(sonarIndex.getResource(foo)).thenReturn(foo);
- when(sonarIndex.getResource(bar)).thenReturn(bar);
+ Resource foo = File.create("src1/Foo.java").setEffectiveKey("foo:src1/Foo.java");
+ Resource bar = File.create("src2/Bar.java").setEffectiveKey("foo:src2/Bar.java");
+ resourceCache.add(foo, null);
+ resourceCache.add(bar, null);
sensorStorage.store(new DefaultDependency()
.from(new DefaultInputFile("foo", "src1/Foo.java").setType(Type.MAIN))
@@ -318,12 +323,14 @@ public class DefaultSensorStorageTest {
@Test
public void shouldIncrementParentWeight() {
- File foo = File.create("src1/Foo.java");
- File bar = File.create("src2/Bar.java");
- Directory src1 = Directory.create("src1");
- Directory src2 = Directory.create("src2");
- when(sonarIndex.getResource(foo)).thenReturn(foo);
- when(sonarIndex.getResource(bar)).thenReturn(bar);
+ Resource src1 = Directory.create("src1").setEffectiveKey("foo:src1");
+ Resource src2 = Directory.create("src2").setEffectiveKey("foo:src2");
+ Resource foo = File.create("src1/Foo.java").setEffectiveKey("foo:src1/Foo.java");
+ Resource bar = File.create("src2/Bar.java").setEffectiveKey("foo:src2/Bar.java");
+ resourceCache.add(src1, null);
+ resourceCache.add(src2, null);
+ resourceCache.add(foo, src1);
+ resourceCache.add(bar, src2);
Dependency parentDep = new Dependency(src1, src2).setWeight(4);
when(sonarIndex.getEdge(src1, src2)).thenReturn(parentDep);
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/no-previous-version.xml b/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/no-previous-version.xml
index 2f01f86b989..480b91f147e 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/no-previous-version.xml
+++ b/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/no-previous-version.xml
@@ -30,13 +30,13 @@
scope="PRJ" qualifier="TRK" created_at="1226235480000" build_date="1226235480000" version="1.2-SNAPSHOT" path=""
status="U" islast="true" depth="0" />
- <events id="2" name="Foo" resource_id="1" snapshot_id="1000" category="Other" event_date="2008-11-03 13:58:00.00" created_at="2008-11-03 13:58:00.00" description=""
+ <events id="2" name="Foo" resource_id="1" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description=""
event_data="[null]"/>
- <events id="4" name="Bar" resource_id="1" snapshot_id="1001" category="Other" event_date="2008-11-05 13:58:00.00" created_at="2008-11-05 13:58:00.00" description=""
+ <events id="4" name="Bar" resource_id="1" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description=""
event_data="[null]"/>
- <events id="5" name="Uhh" resource_id="1" snapshot_id="1002" category="Other" event_date="2008-11-07 13:58:00.00" created_at="2008-11-07 13:58:00.00" description=""
+ <events id="5" name="Uhh" resource_id="1" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description=""
event_data="[null]"/>
- <events id="6" name="1.2-SNAPSHOT" resource_id="1" snapshot_id="1003" category="Version" event_date="2008-11-09 13:58:00.00" created_at="2008-11-09 13:58:00.00" description=""
+ <events id="6" name="1.2-SNAPSHOT" resource_id="1" snapshot_id="1003" category="Version" event_date="1226235480000" created_at="1226235480000" description=""
event_data="[null]"/>
</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version-deleted.xml b/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version-deleted.xml
index d990e648414..bc3a3c9c055 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version-deleted.xml
+++ b/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version-deleted.xml
@@ -30,17 +30,17 @@
scope="PRJ" qualifier="TRK" created_at="1226235480000" build_date="1226235480000" version="1.2-SNAPSHOT" path=""
status="U" islast="true" depth="0" />
- <events id="1" name="1.0" resource_id="1" snapshot_id="1000" category="Version" event_date="2008-11-02 13:58:00.00" created_at="2008-11-02 13:58:00.00" description=""
+ <events id="1" name="1.0" resource_id="1" snapshot_id="1000" category="Version" event_date="1225630680000" created_at="1225630680000" description=""
event_data="[null]"/>
- <events id="2" name="Foo" resource_id="1" snapshot_id="1000" category="Other" event_date="2008-11-03 13:58:00.00" created_at="2008-11-03 13:58:00.00" description=""
+ <events id="2" name="Foo" resource_id="1" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description=""
event_data="[null]"/>
<!-- The "1.1" version was deleted from the history : -->
<!-- events id="3" name="1.1" resource_id="1" snapshot_id="1001" category="Version" event_date="2008-11-04 13:58:00.00" created_at="2008-11-04 13:58:00.00" description=""/-->
- <events id="4" name="Bar" resource_id="1" snapshot_id="1001" category="Other" event_date="2008-11-05 13:58:00.00" created_at="2008-11-05 13:58:00.00" description=""
+ <events id="4" name="Bar" resource_id="1" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description=""
event_data="[null]"/>
- <events id="5" name="Uhh" resource_id="1" snapshot_id="1002" category="Other" event_date="2008-11-07 13:58:00.00" created_at="2008-11-07 13:58:00.00" description=""
+ <events id="5" name="Uhh" resource_id="1" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description=""
event_data="[null]"/>
- <events id="6" name="1.2-SNAPSHOT" resource_id="1" snapshot_id="1003" category="Version" event_date="2008-11-09 13:58:00.00" created_at="2008-11-09 13:58:00.00" description=""
+ <events id="6" name="1.2-SNAPSHOT" resource_id="1" snapshot_id="1003" category="Version" event_date="1226235480000" created_at="1226235480000" description=""
event_data="[null]"/>
</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version.xml b/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version.xml
index 5ecbc2fa959..6efee1a0f51 100644
--- a/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version.xml
+++ b/sonar-batch/src/test/resources/org/sonar/batch/deprecated/components/PastSnapshotFinderByPreviousVersionTest/with-previous-version.xml
@@ -30,11 +30,11 @@
scope="PRJ" qualifier="TRK" created_at="1226235480000" build_date="1226235480000" version="1.2-SNAPSHOT" path=""
status="U" islast="true" depth="0" />
- <events id="1" name="1.0" resource_id="1" snapshot_id="1000" category="Version" event_date="2008-11-02 13:58:00.00" created_at="2008-11-02 13:58:00.00" description="" event_data="[null]"/>
- <events id="2" name="Foo" resource_id="1" snapshot_id="1000" category="Other" event_date="2008-11-03 13:58:00.00" created_at="2008-11-03 13:58:00.00" description="" event_data="[null]"/>
- <events id="3" name="1.1" resource_id="1" snapshot_id="1001" category="Version" event_date="2008-11-04 13:58:00.00" created_at="2008-11-04 13:58:00.00" description="" event_data="[null]"/>
- <events id="4" name="Bar" resource_id="1" snapshot_id="1001" category="Other" event_date="2008-11-05 13:58:00.00" created_at="2008-11-05 13:58:00.00" description="" event_data="[null]"/>
- <events id="5" name="Uhh" resource_id="1" snapshot_id="1002" category="Other" event_date="2008-11-07 13:58:00.00" created_at="2008-11-07 13:58:00.00" description="" event_data="[null]"/>
- <events id="6" name="1.2-SNAPSHOT" resource_id="1" snapshot_id="1003" category="Version" event_date="2008-11-09 13:58:00.00" created_at="2008-11-09 13:58:00.00" description="" event_data="[null]"/>
+ <events id="1" name="1.0" resource_id="1" snapshot_id="1000" category="Version" event_date="1225630680000" created_at="1225630680000" description="" event_data="[null]"/>
+ <events id="2" name="Foo" resource_id="1" snapshot_id="1000" category="Other" event_date="1225717080000" created_at="1225717080000" description="" event_data="[null]"/>
+ <events id="3" name="1.1" resource_id="1" snapshot_id="1001" category="Version" event_date="1225803480000" created_at="1225803480000" description="" event_data="[null]"/>
+ <events id="4" name="Bar" resource_id="1" snapshot_id="1001" category="Other" event_date="1225889880000" created_at="1225889880000" description="" event_data="[null]"/>
+ <events id="5" name="Uhh" resource_id="1" snapshot_id="1002" category="Other" event_date="1226062680000" created_at="1226062680000" description="" event_data="[null]"/>
+ <events id="6" name="1.2-SNAPSHOT" resource_id="1" snapshot_id="1003" category="Version" event_date="1226235480000" created_at="1226235480000" description="" event_data="[null]"/>
</dataset>
diff --git a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
index b9b5fb61955..97977cd2162 100644
--- a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
+++ b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
@@ -22,6 +22,7 @@ package org.sonar.core.config;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Qualifiers;
import org.sonar.core.computation.dbcleaner.DataCleanerProperties;
@@ -118,8 +119,41 @@ public class CorePropertyDefinitions {
.onlyOnQualifiers(Qualifiers.PROJECT)
.category(CoreProperties.CATEGORY_GENERAL)
.subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+ .build(),
+
+ // CPD
+ PropertyDefinition.builder(CoreProperties.CPD_CROSS_PROJECT)
+ .defaultValue(CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE + "")
+ .name("Cross project duplication detection")
+ .description("By default, SonarQube detects duplications at sub-project level. This means that a block "
+ + "duplicated on two sub-projects of the same project won't be reported. Setting this parameter to \"true\" "
+ + "allows to detect duplicates across sub-projects and more generally across projects. Note that activating "
+ + "this property will slightly increase each SonarQube analysis time.")
+ .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
+ .type(PropertyType.BOOLEAN)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.CPD_SKIP_PROPERTY)
+ .defaultValue("false")
+ .name("Skip")
+ .description("Disable detection of duplications")
+ .hidden()
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
+ .type(PropertyType.BOOLEAN)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.CPD_EXCLUSIONS)
+ .defaultValue("")
+ .name("Duplication Exclusions")
+ .description("Patterns used to exclude some source files from the duplication detection mechanism. " +
+ "See below to know how to use wildcards to specify this property.")
+ .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS_EXCLUSIONS)
+ .multiValues(true)
.build()
- ));
+ ));
return defs;
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
index 048e602afe9..7bc6687dd22 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
@@ -33,7 +33,7 @@ import java.util.List;
*/
public class DatabaseVersion implements BatchComponent, ServerComponent {
- public static final int LAST_VERSION = 790;
+ public static final int LAST_VERSION = 794;
/**
* List of all the tables.n
@@ -88,7 +88,7 @@ public class DatabaseVersion implements BatchComponent, ServerComponent {
"user_roles",
"widgets",
"widget_properties"
- );
+ );
private MyBatis mybatis;
public DatabaseVersion(MyBatis mybatis) {
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
index af6b0b2df00..ef5ff49f785 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
@@ -317,6 +317,10 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('787');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('788');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('789');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('790');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('791');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('792');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('793');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('794');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
index 484d9265b83..86d2134ef54 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
@@ -167,8 +167,8 @@ CREATE TABLE "EVENTS" (
"RESOURCE_ID" INTEGER,
"SNAPSHOT_ID" INTEGER,
"CATEGORY" VARCHAR(50),
- "EVENT_DATE" TIMESTAMP,
- "CREATED_AT" TIMESTAMP,
+ "EVENT_DATE" BIGINT NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
"DESCRIPTION" VARCHAR(4000),
"EVENT_DATA" VARCHAR(4000)
);
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index c683d372e66..fe991e780d1 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2884,7 +2884,13 @@ component_viewer.workspace.hide_workspace=Hide workspace
source_viewer.covered=Covered
source_viewer.not_covered=Not covered
source_viewer.conditions=conditions
-source_viewer.expand_duplications=Click to display more in details all the duplicated blocks of this file
+
+source_viewer.tooltip.duplicated_line=This line is duplicated. Click to see duplicated blocks.
+source_viewer.tooltip.duplicated_block=Duplicated block. Click for details.
+source_viewer.tooltip.covered=Fully covered by tests. Click for details.
+source_viewer.tooltip.partially-covered=Partially covered by tests. Click for details.
+source_viewer.tooltip.uncovered=Not covered by tests.
+source_viewer.tooltip.new_code=New {0}.
#------------------------------------------------------------------------------
diff --git a/sonar-core/src/test/java/org/sonar/core/purge/PurgeCommandsTest.java b/sonar-core/src/test/java/org/sonar/core/purge/PurgeCommandsTest.java
index d7d285e42fa..693ea65a447 100644
--- a/sonar-core/src/test/java/org/sonar/core/purge/PurgeCommandsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/purge/PurgeCommandsTest.java
@@ -115,12 +115,10 @@ public class PurgeCommandsTest extends AbstractDaoTestCase {
@Test
public void shouldDeleteResource() {
setupData("shouldDeleteResource");
- SqlSession session = getMyBatis().openSession();
- try {
+ try (SqlSession session = getMyBatis().openSession()) {
new PurgeCommands(session, profiler).deleteResources(newArrayList(new IdUuidPair(1L, "1")));
- } finally {
- MyBatis.closeQuietly(session);
}
+
assertEmptyTables("projects", "snapshots", "events", "issues", "issue_changes", "authors");
}
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml
index e7d8f49a2b4..2f30df757d0 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml
@@ -16,7 +16,7 @@
version="[null]" path="[null]"/>
<events id="1" name="Version 1.0" resource_id="1" snapshot_id="1" category="VERSION" description="[null]"
- event_date="2008-12-02 13:58:00.00" created_at="[null]" event_data="[null]"/>
+ event_date="1228222680000" created_at="1228222680000" event_data="[null]"/>
<issues id="1" kee="ABCDE" component_uuid="1" project_uuid="1" status="CLOSED" resolution="[null]" line="200"
severity="BLOCKER"
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot-result.xml
index 3bac29d413a..f7a0a3a81b3 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot-result.xml
@@ -26,7 +26,7 @@
parent_dependency_id="[null]" project_snapshot_id="1"
dep_usage="USES" dep_weight="1" from_scope="PRJ" to_scope="LIB"/>
<events id="1" name="Version 1.0" resource_id="1" snapshot_id="1" category="VERSION" description="[null]"
- event_date="2008-12-02 13:58:00.00" created_at="[null]" event_data="[null]"/>
+ event_date="1228222680000" created_at="1228222680000" event_data="[null]"/>
<duplications_index id="1" project_snapshot_id="1" snapshot_id="1" hash="bb" index_in_file="0" start_line="0"
end_line="0"/>
</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot.xml
index 8fd273d1c08..45eaee58163 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteSnapshot.xml
@@ -25,7 +25,7 @@
parent_dependency_id="[null]" project_snapshot_id="1"
dep_usage="USES" dep_weight="1" from_scope="PRJ" to_scope="LIB"/>
<events id="1" name="Version 1.0" resource_id="1" snapshot_id="1" category="VERSION" description="[null]"
- event_date="2008-12-02 13:58:00.00" created_at="[null]" event_data="[null]"/>
+ event_date="1228222680000" created_at="1228222680000" event_data="[null]"/>
<duplications_index id="1" project_snapshot_id="1" snapshot_id="1" hash="bb" index_in_file="0" start_line="0"
end_line="0"/>
@@ -58,7 +58,7 @@
parent_dependency_id="[null]" project_snapshot_id="5"
dep_usage="USES" dep_weight="1" from_scope="PRJ" to_scope="LIB"/>
<events id="2" name="Version 1.0" resource_id="5" snapshot_id="5" category="VERSION" description="[null]"
- event_date="2008-12-02 13:58:00.00" created_at="[null]" event_data="[null]"/>
+ event_date="1228222680000" created_at="1228222680000" event_data="[null]"/>
<duplications_index id="2" project_snapshot_id="5" snapshot_id="5" hash="bb" index_in_file="0" start_line="0"
end_line="0"/>
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot-result.xml
index bcd5544b9f6..4a45a7fc405 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot-result.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot-result.xml
@@ -42,8 +42,8 @@ Note that measures, events and reviews are not deleted.
<!--dep_usage="USES" dep_weight="1" from_scope="LIB" to_scope="PRJ"/>-->
<events id="1" resource_id="1" snapshot_id="1"
- category="VERSION" description="[null]" name="Version 1.0" event_date="2008-12-02 13:58:00.00"
- created_at="[null]"
+ category="VERSION" description="[null]" name="Version 1.0" event_date="1228222680000"
+ created_at="1228222680000"
event_data="[null]"/>
<!--<duplications_index id="1" project_snapshot_id="1" snapshot_id="1"-->
@@ -81,8 +81,8 @@ Note that measures, events and reviews are not deleted.
dep_usage="USES" dep_weight="1" from_scope="LIB" to_scope="PRJ"/>
<events id="2" resource_id="2" snapshot_id="2"
- category="VERSION" description="[null]" name="Version 1.0" event_date="2008-12-02 13:58:00.00"
- created_at="[null]"
+ category="VERSION" description="[null]" name="Version 1.0" event_date="1228222680000"
+ created_at="1228222680000"
event_data="[null]"/>
<duplications_index id="2" project_snapshot_id="2" snapshot_id="2"
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot.xml
index 5a576b3edf8..6c6e5bd44c2 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldPurgeSnapshot.xml
@@ -27,8 +27,8 @@
dep_usage="USES" dep_weight="1" from_scope="LIB" to_scope="PRJ"/>
<events id="1" resource_id="1" snapshot_id="1"
- category="VERSION" description="[null]" name="Version 1.0" event_date="2008-12-02 13:58:00.00"
- created_at="[null]"
+ category="VERSION" description="[null]" name="Version 1.0" event_date="1228222680000"
+ created_at="1228222680000"
event_data="[null]"/>
<duplications_index id="1" project_snapshot_id="1" snapshot_id="1"
@@ -65,8 +65,8 @@
dep_usage="USES" dep_weight="1" from_scope="LIB" to_scope="PRJ"/>
<events id="2" resource_id="2" snapshot_id="2"
- category="VERSION" description="[null]" name="Version 1.0" event_date="2008-12-02 13:58:00.00"
- created_at="[null]"
+ category="VERSION" description="[null]" name="Version 1.0" event_date="1228222680000"
+ created_at="1228222680000"
event_data="[null]"/>
<duplications_index id="2" project_snapshot_id="2" snapshot_id="2"
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldSelectPurgeableSnapshots.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldSelectPurgeableSnapshots.xml
index f3a3b9bd0b3..78e14ead2a7 100644
--- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldSelectPurgeableSnapshots.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldSelectPurgeableSnapshots.xml
@@ -56,7 +56,7 @@
depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000" version="[null]" path="[null]"/>
<events id="2" resource_id="1" snapshot_id="5"
- category="Version" description="[null]" name="Version 1.0" event_date="2008-12-02 13:58:00.00" created_at="[null]"
+ category="Version" description="[null]" name="Version 1.0" event_date="1228222680000" created_at="1228222680000"
event_data="[null]"/>
</dataset>
diff --git a/sonar-graph/src/main/java/org/sonar/graph/Dsm.java b/sonar-graph/src/main/java/org/sonar/graph/Dsm.java
index 4d8673d1992..811cad56e3e 100644
--- a/sonar-graph/src/main/java/org/sonar/graph/Dsm.java
+++ b/sonar-graph/src/main/java/org/sonar/graph/Dsm.java
@@ -31,7 +31,6 @@ public class Dsm<V> {
private final DsmCell[][] cells;
private final int dimension;
private final DirectedGraphAccessor<V, ? extends Edge<V>> graph;
- private boolean atLeastOneDependency = false;
public Dsm(DirectedGraphAccessor<V, ? extends Edge<V>> graph, Collection<V> vertices, Set<Edge> feedbackEdges) {
this.graph = graph;
@@ -57,7 +56,6 @@ public class Dsm<V> {
Edge<V> edge = graph.getEdge(from, to);
if (edge != null) {
- atLeastOneDependency = true;
boolean isFeedbackEdge = feedbackEdges.contains(edge);
result[x][y] = new DsmCell(edge, isFeedbackEdge);
}
@@ -171,13 +169,6 @@ public class Dsm<V> {
return cells[x][y];
}
- /**
- * @since 5.0
- */
- public boolean hasAtLeastOneDependency() {
- return atLeastOneDependency;
- }
-
public V[] getVertices() {
V[] verticesCopy = (V[]) new Object[vertices.length];
System.arraycopy(vertices, 0, verticesCopy, 0, vertices.length);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java
index 8c802c2ca98..eb4db71fe41 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java
@@ -26,6 +26,9 @@ import org.sonar.api.database.model.Snapshot;
import javax.persistence.*;
import java.util.Date;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.sonar.api.utils.DateUtils.longToDate;
+
/**
* @since 1.10
*/
@@ -46,10 +49,10 @@ public class Event extends BaseIdentifiable {
private String category;
@Column(name = "event_date", updatable = true, nullable = false)
- private Date date;
+ private Long date;
- @Column(name = "created_at", updatable = true, nullable = true)
- private Date createdAt;
+ @Column(name = "created_at", updatable = true, nullable = false)
+ private Long createdAt;
@Column(name = "event_data", updatable = true, nullable = true)
private String data;
@@ -103,11 +106,11 @@ public class Event extends BaseIdentifiable {
}
public Date getDate() {
- return date;
+ return longToDate(date);
}
public void setDate(Date date) {
- this.date = date;
+ this.date = date.getTime();
}
public Snapshot getSnapshot() {
@@ -115,19 +118,17 @@ public class Event extends BaseIdentifiable {
}
public Date getCreatedAt() {
- return createdAt;
+ return new Date(createdAt);
}
public void setCreatedAt(Date createdAt) {
- this.createdAt = createdAt;
+ this.createdAt = createdAt.getTime();
}
public final void setSnapshot(Snapshot snapshot) {
- this.snapshot = snapshot;
- if (snapshot != null) {
- this.date = (snapshot.getCreatedAtMs() == null ? null : new Date(snapshot.getCreatedAtMs()));
- this.resourceId = snapshot.getResourceId();
- }
+ this.snapshot = checkNotNull(snapshot, "it is not possible to set a null snapshot linked to an event");
+ this.date = snapshot.getCreatedAtMs();
+ this.resourceId = snapshot.getResourceId();
}
public Integer getResourceId() {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
index b75209e1538..6a5463e67f9 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
@@ -30,6 +30,7 @@ import org.sonar.api.resources.Resource;
import org.sonar.api.rules.Violation;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.Collection;
@@ -191,14 +192,23 @@ public interface SensorContext extends org.sonar.api.batch.sensor.SensorContext
// ----------- DEPENDENCIES BETWEEN RESOURCES --------------
/**
- * Build a new dependency : from depends upon to. The dependency is NOT saved. The method saveDependency() must still be executed.
+ * @deprecated since 5.1 use {@link #newDependency()}
*/
Dependency saveDependency(Dependency dependency);
+ /**
+ * @deprecated since 5.1 Sensors should not read but only save data
+ */
Set<Dependency> getDependencies();
+ /**
+ * @deprecated since 5.1 Sensors should not read but only save data
+ */
Collection<Dependency> getIncomingDependencies(Resource to);
+ /**
+ * @deprecated since 5.1 Sensors should not read but only save data
+ */
Collection<Dependency> getOutgoingDependencies(Resource from);
// ----------- FILE SOURCES --------------
@@ -242,7 +252,7 @@ public interface SensorContext extends org.sonar.api.batch.sensor.SensorContext
* @param date the event date
* @return the created event
*/
- Event createEvent(Resource resource, String name, String description, String category, Date date);
+ Event createEvent(Resource resource, String name, @Nullable String description, String category, @Nullable Date date);
/**
* Deletes an event
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
index f21b83bab5f..6714ed9c1bb 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
@@ -30,6 +30,7 @@ import org.sonar.api.rules.Violation;
import org.sonar.graph.DirectedGraphAccessor;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Date;
@@ -155,7 +156,7 @@ public abstract class SonarIndex implements DirectedGraphAccessor<Resource, Depe
public abstract void deleteEvent(Event event);
- public abstract Event addEvent(Resource resource, String name, String description, String category, Date date);
+ public abstract Event addEvent(Resource resource, String name, String description, String category, @Nullable Date date);
public final Collection<Dependency> getOutgoingDependencies(Resource from) {
return getOutgoingEdges(from);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
index b36786c2333..a1de94d8b3f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
@@ -24,7 +24,7 @@ import org.sonar.api.batch.CpdMapping;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.sensor.dependency.Dependency;
+import org.sonar.api.batch.sensor.dependency.NewDependency;
import org.sonar.api.batch.sensor.duplication.NewDuplication;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
@@ -82,7 +82,6 @@ public interface SensorContext {
/**
* Builder to define highlighting of a file.
- * @since 4.5
*/
HighlightingBuilder highlightingBuilder(InputFile inputFile);
@@ -90,7 +89,6 @@ public interface SensorContext {
/**
* Builder to define symbol references in a file.
- * @since 4.5
*/
SymbolTableBuilder symbolTableBuilder(InputFile inputFile);
@@ -99,7 +97,6 @@ public interface SensorContext {
/**
* Builder to manually register duplication in a file. This can be used in addition to {@link CpdMapping} extension point.
* Don't forget to call {@link NewDuplication#save()}.
- * @since 5.1
*/
NewDuplication newDuplication();
@@ -108,21 +105,18 @@ public interface SensorContext {
/**
* Create a new coverage report.
* Don't forget to call {@link Coverage#save()} once all parameters are provided.
- * @since 5.0
*/
Coverage newCoverage();
/**
* Create a new test case execution report.
* Don't forget to call {@link TestCaseExecution#save()} once all parameters are provided.
- * @since 5.0
*/
TestCaseExecution newTestCaseExecution();
/**
* Create a new test case coverage report.
* Don't forget to call {@link TestCaseCoverage#save()} once all parameters are provided.
- * @since 5.0
*/
TestCaseCoverage newTestCaseCoverage();
@@ -130,9 +124,8 @@ public interface SensorContext {
/**
* Create a new dependency.
- * Don't forget to call {@link Dependency#save()} once all parameters are provided.
- * @since 5.0
+ * Don't forget to call {@link NewDependency#save()} once all parameters are provided.
*/
- Dependency newDependency();
+ NewDependency newDependency();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java
index 87bd3029fbd..56185acaf3a 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/Dependency.java
@@ -19,36 +19,19 @@
*/
package org.sonar.api.batch.sensor.dependency;
-import com.google.common.annotations.Beta;
-import org.sonar.api.batch.fs.InputFile;
/**
- * @since 5.0
+ * @since 5.1
*/
-@Beta
public interface Dependency {
- InputFile from();
+ String fromKey();
- Dependency from(InputFile from);
-
- InputFile to();
-
- Dependency to(InputFile to);
+ String toKey();
/**
* Default weight value is 1.
*/
int weight();
- /**
- * Set the weight of the dependency.
- */
- Dependency weight(int weight);
-
- /**
- * Save the dependency.
- */
- void save();
-
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/NewDependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/NewDependency.java
new file mode 100644
index 00000000000..8d62a2f6516
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/NewDependency.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.sensor.dependency;
+
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * Builder to create new Dependency.
+ * Should not be implemented by client.
+ * @since 5.1
+ */
+public interface NewDependency {
+
+ NewDependency from(InputFile from);
+
+ NewDependency to(InputFile to);
+
+ /**
+ * Set the weight of the dependency. If not set default weight is 1.
+ */
+ NewDependency weight(int weight);
+
+ /**
+ * Save the dependency. It is not permitted so save several time a dependency between two same files.
+ */
+ void save();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java
index c7ab2fcc997..9dcb3d34ece 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/dependency/internal/DefaultDependency.java
@@ -23,16 +23,18 @@ import com.google.common.base.Preconditions;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.SensorStorage;
import org.sonar.api.batch.sensor.dependency.Dependency;
+import org.sonar.api.batch.sensor.dependency.NewDependency;
import org.sonar.api.batch.sensor.internal.DefaultStorable;
import javax.annotation.Nullable;
-public class DefaultDependency extends DefaultStorable implements Dependency {
+public class DefaultDependency extends DefaultStorable implements Dependency, NewDependency {
- private InputFile from;
- private InputFile to;
+ private String fromKey;
+ private String toKey;
private int weight = 1;
public DefaultDependency() {
@@ -44,21 +46,21 @@ public class DefaultDependency extends DefaultStorable implements Dependency {
}
@Override
- public Dependency from(InputFile from) {
+ public DefaultDependency from(InputFile from) {
Preconditions.checkNotNull(from, "InputFile should be non null");
- this.from = from;
+ this.fromKey = ((DefaultInputFile) from).key();
return this;
}
@Override
- public Dependency to(InputFile to) {
+ public DefaultDependency to(InputFile to) {
Preconditions.checkNotNull(to, "InputFile should be non null");
- this.to = to;
+ this.toKey = ((DefaultInputFile) to).key();
return this;
}
@Override
- public Dependency weight(int weight) {
+ public DefaultDependency weight(int weight) {
Preconditions.checkArgument(weight > 1, "weight should be greater than 1");
this.weight = weight;
return this;
@@ -66,20 +68,30 @@ public class DefaultDependency extends DefaultStorable implements Dependency {
@Override
public void doSave() {
- Preconditions.checkState(!this.from.equals(this.to), "From and To can't be the same inputFile");
- Preconditions.checkNotNull(this.from, "From inputFile can't be null");
- Preconditions.checkNotNull(this.to, "To inputFile can't be null");
+ Preconditions.checkState(!this.fromKey.equals(this.toKey), "From and To can't be the same inputFile");
+ Preconditions.checkNotNull(this.fromKey, "From inputFile can't be null");
+ Preconditions.checkNotNull(this.toKey, "To inputFile can't be null");
storage.store((Dependency) this);
}
@Override
- public InputFile from() {
- return this.from;
+ public String fromKey() {
+ return this.fromKey;
+ }
+
+ public DefaultDependency setFromKey(String fromKey) {
+ this.fromKey = fromKey;
+ return this;
}
@Override
- public InputFile to() {
- return this.to;
+ public String toKey() {
+ return this.toKey;
+ }
+
+ public DefaultDependency setToKey(String toKey) {
+ this.toKey = toKey;
+ return this;
}
@Override
@@ -87,6 +99,11 @@ public class DefaultDependency extends DefaultStorable implements Dependency {
return this.weight;
}
+ public DefaultDependency setWeight(int weight) {
+ this.weight = weight;
+ return this;
+ }
+
// For testing purpose
@Override
@@ -102,8 +119,8 @@ public class DefaultDependency extends DefaultStorable implements Dependency {
}
DefaultDependency rhs = (DefaultDependency) obj;
return new EqualsBuilder()
- .append(from, rhs.from)
- .append(to, rhs.to)
+ .append(fromKey, rhs.fromKey)
+ .append(toKey, rhs.toKey)
.append(weight, rhs.weight)
.isEquals();
}
@@ -111,8 +128,8 @@ public class DefaultDependency extends DefaultStorable implements Dependency {
@Override
public int hashCode() {
return new HashCodeBuilder(27, 45).
- append(from).
- append(to).
+ append(fromKey).
+ append(toKey).
append(weight).
toHashCode();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/License.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/License.java
index 0e0f316d532..d0f53d44024 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/config/License.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/License.java
@@ -102,7 +102,15 @@ public final class License {
@VisibleForTesting
boolean isExpired(Date now) {
Date date = getExpirationDate();
- return date != null && !date.after(org.apache.commons.lang.time.DateUtils.truncate(now, Calendar.DATE));
+ if (date == null) {
+ return false;
+ }
+ // SONAR-6079 include last day
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.SECOND, -1);
+ return now.after(cal.getTime());
}
@Nullable
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java
index 5b7537f20da..bc965583881 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java
@@ -50,11 +50,25 @@ public class Dependency implements Edge<Resource> {
return from;
}
+ /**
+ * For internal use only
+ */
+ public void setFrom(Resource from) {
+ this.from = from;
+ }
+
@Override
public Resource getTo() {
return to;
}
+ /**
+ * For internal use only
+ */
+ public void setTo(Resource to) {
+ this.to = to;
+ }
+
public String getUsage() {
return usage;
}
@@ -105,26 +119,26 @@ public class Dependency implements Edge<Resource> {
}
Dependency other = (Dependency) obj;
return new EqualsBuilder()
- .append(from, other.from)
- .append(to, other.to)
- .isEquals();
+ .append(from, other.from)
+ .append(to, other.to)
+ .isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
- .append(from)
- .append(to)
- .toHashCode();
+ .append(from)
+ .append(to)
+ .toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .append("from", from)
- .append("to", to)
- .append("weight", weight)
- .append("usage", usage)
- .toString();
+ .append("from", from)
+ .append("to", to)
+ .append("weight", weight)
+ .append("usage", usage)
+ .toString();
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java
index 19ce2aa8fc8..80d273a13ed 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java
@@ -120,8 +120,17 @@ public final class ResourceUtils {
/**
* @return whether a resource is a unit test class
+ * @deprecated since 5.1 use {@link #isUnitTestFile(Resource)}
*/
+ @Deprecated
public static boolean isUnitTestClass(Resource resource) {
+ return isUnitTestFile(resource);
+ }
+
+ /**
+ * @return whether a resource is a unit test class
+ */
+ public static boolean isUnitTestFile(Resource resource) {
return Qualifiers.UNIT_TEST_FILE.equals(resource.getQualifier());
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
index 4ddf32cd622..f26cad3ae4c 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RulesDefinition.java
@@ -293,6 +293,61 @@ public interface RulesDefinition extends ServerExtension {
*/
public static final String UNIT_TESTABILITY = "UNIT_TESTABILITY";
+ /**
+ * Related to characteristic ACCESSIBILITY
+ */
+ public static final String USABILITY_ACCESSIBILITY = "USABILITY_ACCESSIBILITY";
+
+ /**
+ * Related to characteristic ACCESSIBILITY
+ */
+ public static final String USABILITY_COMPLIANCE = "USABILITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic ACCESSIBILITY
+ */
+ public static final String USABILITY_EASE_OF_USE = "USABILITY_EASE_OF_USE";
+
+ /**
+ * Related to characteristic REUSABILITY
+ */
+ public static final String REUSABILITY_COMPLIANCE = "REUSABILITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic PORTABILITY
+ */
+ public static final String PORTABILITY_COMPLIANCE = "PORTABILITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic MAINTAINABILITY
+ */
+ public static final String MAINTAINABILITY_COMPLIANCE = "MAINTAINABILITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic SECURITY
+ */
+ public static final String SECURITY_COMPLIANCE = "SECURITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic EFFICIENCY
+ */
+ public static final String EFFICIENCY_COMPLIANCE = "EFFICIENCY_COMPLIANCE";
+
+ /**
+ * Related to characteristic CHANGEABILITY
+ */
+ public static final String CHANGEABILITY_COMPLIANCE = "CHANGEABILITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic RELIABILITY
+ */
+ public static final String RELIABILITY_COMPLIANCE = "RELIABILITY_COMPLIANCE";
+
+ /**
+ * Related to characteristic TESTABILITY
+ */
+ public static final String TESTABILITY_COMPLIANCE = "TESTABILITY_COMPLIANCE";
+
private SubCharacteristics() {
// only constants
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/LicenseTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/LicenseTest.java
index 305132e5251..ed490dd9ee7 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/config/LicenseTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/LicenseTest.java
@@ -23,6 +23,9 @@ import org.apache.commons.codec.binary.Base64;
import org.junit.Test;
import org.sonar.api.utils.DateUtils;
+import java.util.Calendar;
+import java.util.TimeZone;
+
import static org.assertj.core.api.Assertions.assertThat;
public class LicenseTest {
@@ -131,10 +134,20 @@ public class LicenseTest {
public void isExpired() {
License license = License.readPlainText(V2_FORMAT);
- assertThat(license.isExpired(DateUtils.parseDate("2013-06-23"))).isTrue();
- assertThat(license.isExpired(DateUtils.parseDate("2012-05-18"))).isTrue();
- assertThat(license.isExpired(DateUtils.parseDateTime("2012-05-18T15:50:45+0100"))).isTrue();
assertThat(license.isExpired(DateUtils.parseDate("2011-01-01"))).isFalse();
+ Calendar sameDay = Calendar.getInstance(TimeZone.getDefault());
+ sameDay.setTime(DateUtils.parseDate("2012-05-18"));
+ assertThat(license.isExpired(sameDay.getTime())).isFalse();
+ sameDay.set(Calendar.HOUR_OF_DAY, 15);
+ assertThat(license.isExpired(sameDay.getTime())).isFalse();
+ sameDay.set(Calendar.HOUR_OF_DAY, 23);
+ sameDay.set(Calendar.MINUTE, 59);
+ sameDay.set(Calendar.SECOND, 59);
+ assertThat(license.isExpired(sameDay.getTime())).isFalse();
+ // The day after
+ sameDay.add(Calendar.SECOND, 1);
+ assertThat(license.isExpired(sameDay.getTime())).isTrue();
+ assertThat(license.isExpired(DateUtils.parseDate("2013-06-23"))).isTrue();
}
@Test