Browse Source

SONAR-6721 Prevent running concurrent batch processes on the same physical project

tags/5.2-RC1
Duarte Meneses 8 years ago
parent
commit
7370c19e9a

+ 13
- 2
it/it-tests/src/test/java/batch/IssuesModeTest.java View File

@@ -295,7 +295,6 @@ public class IssuesModeTest {
fail("Issue not found");
}

// SONAR-4602
@Test
public void concurrent_issue_mode_on_existing_project() throws Exception {
restoreProfile("one-issue-per-line.xml");
@@ -312,7 +311,7 @@ public class IssuesModeTest {
// Install sonar-runner in advance to avoid concurrent unzip issues
FileSystem fileSystem = orchestrator.getConfiguration().fileSystem();
new SonarRunnerInstaller(fileSystem).install(Version.create(SonarRunner.DEFAULT_RUNNER_VERSION), fileSystem.workspace());
final int nThreads = 5;
final int nThreads = 3;
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
List<Callable<BuildResult>> tasks = new ArrayList<>();
for (int i = 0; i < nThreads; i++) {
@@ -325,8 +324,20 @@ public class IssuesModeTest {
});
}

boolean expectedError = false;
for (Future<BuildResult> result : executorService.invokeAll(tasks)) {
try {
result.get();
} catch(ExecutionException e) {
if(e.getCause() instanceof BuildFailureException) {
BuildFailureException bfe = (BuildFailureException) e.getCause();
assertThat(bfe.getResult().getLogs()).contains("Another SonarQube analysis is already in progress for this project");
expectedError = true;
}
}
}
if(!expectedError) {
fail("At least one of the threads should have failed");
}
}


+ 35
- 22
sonar-batch/src/main/java/org/sonar/batch/analysis/AnalysisTempFolderProvider.java View File

@@ -19,49 +19,62 @@
*/
package org.sonar.batch.analysis;

import org.sonar.batch.bootstrap.LifecycleProviderAdapter;

import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.picocontainer.PicoContainer;
import org.picocontainer.ComponentLifecycle;
import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.api.utils.TempFolder;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.utils.internal.DefaultTempFolder;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class AnalysisTempFolderProvider extends LifecycleProviderAdapter {
public class AnalysisTempFolderProvider extends ProviderAdapter implements ComponentLifecycle<TempFolder> {
static final String TMP_NAME = ".sonartmp";
private DefaultTempFolder projectTempFolder;

public TempFolder provide(AnalysisProperties props) {
public TempFolder provide(ProjectReactor projectReactor) {
if (projectTempFolder == null) {
String workingDirPath = StringUtils.defaultIfBlank(props.property(CoreProperties.WORKING_DIRECTORY), CoreProperties.WORKING_DIRECTORY_DEFAULT_VALUE);
Path workingDir = Paths.get(workingDirPath).normalize();

if (!workingDir.isAbsolute()) {
Path base = getBasePath(props);
workingDir = base.resolve(workingDir);
}

Path tempDir = workingDir.resolve(TMP_NAME);
Path workingDir = projectReactor.getRoot().getWorkDir().toPath();
Path tempDir = workingDir.normalize().resolve(TMP_NAME);
try {
Files.deleteIfExists(tempDir);
Files.createDirectories(tempDir);
} catch (IOException e) {
throw new IllegalStateException("Unable to create root temp directory " + tempDir, e);
}

projectTempFolder = new DefaultTempFolder(tempDir.toFile(), true);
this.instance = projectTempFolder;
}
return projectTempFolder;
}

private static Path getBasePath(AnalysisProperties props) {
String baseDir = props.property("sonar.projectBaseDir");
if (baseDir == null) {
throw new IllegalStateException("sonar.projectBaseDir needs to be specified");
@Override
public void start(PicoContainer container) {
started = true;
}

private boolean started = false;

@Override
public void stop(PicoContainer container) {
if (projectTempFolder != null) {
projectTempFolder.stop();
}
return Paths.get(baseDir);
}

@Override
public void dispose(PicoContainer container) {
}

@Override
public boolean componentHasLifecycle() {
return true;
}

@Override
public boolean isStarted() {
return started;
}
}

+ 39
- 9
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalTempFolderProvider.java View File

@@ -19,6 +19,10 @@
*/
package org.sonar.batch.bootstrap;

import org.picocontainer.ComponentLifecycle;

import org.picocontainer.PicoContainer;
import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
@@ -36,10 +40,9 @@ import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.TimeUnit;

public class GlobalTempFolderProvider extends LifecycleProviderAdapter {
public class GlobalTempFolderProvider extends ProviderAdapter implements ComponentLifecycle<TempFolder> {
private static final Logger LOG = Loggers.get(GlobalTempFolderProvider.class);
private static final long CLEAN_MAX_AGE = TimeUnit.DAYS.toMillis(21);

static final String TMP_NAME_PREFIX = ".sonartmp_";

private System2 system;
@@ -57,10 +60,10 @@ public class GlobalTempFolderProvider extends LifecycleProviderAdapter {
if (tempFolder == null) {

String workingPathName = StringUtils.defaultIfBlank(bootstrapProps.property(CoreProperties.GLOBAL_WORKING_DIRECTORY), CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE);
Path workingPath = Paths.get(workingPathName).normalize();
Path workingPath = Paths.get(workingPathName);

if (!workingPath.isAbsolute()) {
Path home = findHome(bootstrapProps);
Path home = findSonarHome(bootstrapProps);
workingPath = home.resolve(workingPath).normalize();
}

@@ -71,7 +74,6 @@ public class GlobalTempFolderProvider extends LifecycleProviderAdapter {
}
Path tempDir = createTempFolder(workingPath);
tempFolder = new DefaultTempFolder(tempDir.toFile(), true);
this.instance = tempFolder;
}
return tempFolder;
}
@@ -90,20 +92,20 @@ public class GlobalTempFolderProvider extends LifecycleProviderAdapter {
}
}

private Path findHome(GlobalProperties props) {
private Path findSonarHome(GlobalProperties props) {
String home = props.property("sonar.userHome");
if (home != null) {
return Paths.get(home);
return Paths.get(home).toAbsolutePath();
}

home = system.envVariable("SONAR_USER_HOME");

if (home != null) {
return Paths.get(home);
return Paths.get(home).toAbsolutePath();
}

home = system.property("user.home");
return Paths.get(home, ".sonar");
return Paths.get(home, ".sonar").toAbsolutePath();
}

private static void cleanTempFolders(Path path) throws IOException {
@@ -143,4 +145,32 @@ public class GlobalTempFolderProvider extends LifecycleProviderAdapter {
return creationTime < threshold;
}
}

@Override
public void start(PicoContainer container) {
started = true;
}

private boolean started = false;

@Override
public void stop(PicoContainer container) {
if (tempFolder != null) {
tempFolder.stop();
}
}

@Override
public void dispose(PicoContainer container) {
}

@Override
public boolean componentHasLifecycle() {
return true;
}

@Override
public boolean isStarted() {
return started;
}
}

+ 0
- 85
sonar-batch/src/main/java/org/sonar/batch/bootstrap/LifecycleProviderAdapter.java View File

@@ -1,85 +0,0 @@
/*
* 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.bootstrap;

import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
import org.picocontainer.monitors.NullComponentMonitor;

import org.picocontainer.LifecycleStrategy;
import org.picocontainer.PicoContainer;
import org.picocontainer.ComponentLifecycle;
import org.picocontainer.injectors.ProviderAdapter;
import org.picocontainer.Startable;

public abstract class LifecycleProviderAdapter extends ProviderAdapter implements Startable, ComponentLifecycle<Object> {
private LifecycleStrategy lifecycleStrategy;
protected Object instance;

public LifecycleProviderAdapter() {
this(new ReflectionLifecycleStrategy(new NullComponentMonitor()));
}

public LifecycleProviderAdapter(LifecycleStrategy lifecycleStrategy) {
this.lifecycleStrategy = lifecycleStrategy;
}

@Override
public final void start() {
if (instance != null) {
lifecycleStrategy.start(instance);
}
}

@Override
public final void stop() {
if (instance != null) {
lifecycleStrategy.stop(instance);
}
}

@Override
public void start(PicoContainer container) {
start();
started = true;
}

@Override
public void stop(PicoContainer container) {
stop();
started = false;
}

@Override
public void dispose(PicoContainer container) {
}

@Override
public boolean componentHasLifecycle() {
return true;
}

@Override
public boolean isStarted() {
return started;
}

private boolean started = false;

}

+ 16
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/MutableProjectReactorProvider.java View File

@@ -19,15 +19,18 @@
*/
package org.sonar.batch.scan;

import org.apache.commons.io.FileUtils;
import org.sonar.batch.analysis.AnalysisProperties;

import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.utils.SonarException;

import javax.annotation.Nullable;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class MutableProjectReactorProvider extends ProviderAdapter {

private final ProjectBootstrapper projectBootstrapper;
@@ -49,9 +52,19 @@ public class MutableProjectReactorProvider extends ProviderAdapter {
reactor = projectBootstrapper.bootstrap();
}
if (reactor == null) {
throw new SonarException(projectBootstrapper + " has returned null as ProjectReactor");
throw new IllegalStateException(projectBootstrapper + " has returned null as ProjectReactor");
}
cleanDirectory(reactor.getRoot().getWorkDir());
}
return reactor;
}

private void cleanDirectory(File dir) {
try {
FileUtils.deleteDirectory(dir);
Files.createDirectories(dir.toPath());
} catch (IOException e) {
throw new IllegalStateException("Failed to recreate working directory: " + dir.getAbsolutePath(), e);
}
}
}

+ 101
- 0
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java View File

@@ -0,0 +1,101 @@
/*
* 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.scan;

import org.picocontainer.Startable;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.Files;
import java.nio.file.Path;

public class ProjectLock implements Startable {
private static final Logger LOG = LoggerFactory.getLogger(ProjectLock.class);
static final String LOCK_FILE_NAME = ".sonar_lock";
private final Path lockFilePath;

private RandomAccessFile lockRandomAccessFile;
private FileChannel lockChannel;
private FileLock lockFile;

public ProjectLock(ProjectReactor projectReactor) {
Path directory = projectReactor.getRoot().getBaseDir().toPath();
this.lockFilePath = directory.resolve(LOCK_FILE_NAME).toAbsolutePath();
}

public void tryLock() {
try {
lockRandomAccessFile = new RandomAccessFile(lockFilePath.toFile(), "rw");
lockChannel = lockRandomAccessFile.getChannel();
lockFile = lockChannel.tryLock(0, 1024, false);

if (lockFile == null) {
failAlreadyInProgress();
}
} catch (OverlappingFileLockException e) {
failAlreadyInProgress();
} catch (IOException e) {
throw new IllegalStateException("Failed to create project lock in " + lockFilePath.toString(), e);
}
}
private static void failAlreadyInProgress() {
throw new IllegalStateException("Another SonarQube analysis is already in progress for this project");
}

public void stop() {
if (lockFile != null) {
try {
Files.delete(lockFilePath);
lockFile.release();
lockFile = null;
} catch (IOException e) {
LOG.error("Error releasing lock", e);
}
}
if (lockChannel != null) {
try {
lockChannel.close();
lockChannel = null;
} catch (IOException e) {
LOG.error("Error closing file channel", e);
}
}
if (lockRandomAccessFile != null) {
try {
lockRandomAccessFile.close();
lockRandomAccessFile = null;
} catch (IOException e) {
LOG.error("Error closing file", e);
}
}
}

@Override
public void start() {
}

}

+ 2
- 3
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java View File

@@ -20,7 +20,6 @@
package org.sonar.batch.scan;

import org.sonar.batch.analysis.DefaultAnalysisMode;

import org.sonar.batch.analysis.AnalysisWSLoaderProvider;
import org.sonar.batch.analysis.AnalysisTempFolderProvider;
import org.sonar.batch.analysis.AnalysisProperties;
@@ -51,7 +50,6 @@ import org.sonar.batch.bootstrap.ExtensionMatcher;
import org.sonar.batch.bootstrap.ExtensionUtils;
import org.sonar.batch.bootstrap.MetricProvider;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.deprecated.components.DefaultResourceCreationLock;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.events.EventBus;
import org.sonar.batch.index.BatchComponentCache;
@@ -107,6 +105,7 @@ public class ProjectScanContainer extends ComponentContainer {
add(component);
}
addBatchComponents();
getComponentByType(ProjectLock.class).tryLock();
addBatchExtensions();
Settings settings = getComponentByType(Settings.class);
if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
@@ -135,6 +134,7 @@ public class ProjectScanContainer extends ComponentContainer {
new MutableProjectReactorProvider(getComponentByType(ProjectBootstrapper.class)),
new ImmutableProjectReactorProvider(),
ProjectBuildersExecutor.class,
ProjectLock.class,
EventBus.class,
PhasesTimeProfiler.class,
ResourceTypes.class,
@@ -143,7 +143,6 @@ public class ProjectScanContainer extends ComponentContainer {
ProjectReactorValidator.class,
new ProjectRepositoriesProvider(),
new AnalysisWSLoaderProvider(),
DefaultResourceCreationLock.class,
CodeColorizers.class,
MetricProvider.class,
ProjectConfigurator.class,

+ 21
- 27
sonar-batch/src/test/java/org/sonar/batch/analysis/AnalysisTempFolderProviderTest.java View File

@@ -19,20 +19,21 @@
*/
package org.sonar.batch.analysis;

import org.sonar.batch.analysis.AnalysisTempFolderProvider;
import org.sonar.api.batch.bootstrap.ProjectDefinition;

import org.sonar.batch.analysis.AnalysisProperties;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.junit.Before;
import org.sonar.batch.analysis.AnalysisTempFolderProvider;
import org.sonar.api.utils.TempFolder;
import org.apache.commons.io.FileUtils;
import com.google.common.collect.ImmutableMap;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.CoreProperties;

import java.io.File;
import java.io.IOException;

import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
import static org.assertj.core.api.Assertions.assertThat;

public class AnalysisTempFolderProviderTest {
@@ -40,33 +41,26 @@ public class AnalysisTempFolderProviderTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();

private AnalysisTempFolderProvider tempFolderProvider = new AnalysisTempFolderProvider();

@Test
public void createTempFolderWithProps() throws Exception {
File workingDir = temp.newFolder();
File tmpDir = new File(workingDir, AnalysisTempFolderProvider.TMP_NAME);
private AnalysisTempFolderProvider tempFolderProvider;
private ProjectReactor projectReactor;

TempFolder tempFolder = tempFolderProvider.provide(new AnalysisProperties(ImmutableMap.of(CoreProperties.WORKING_DIRECTORY, workingDir.getAbsolutePath()), ""));
tempFolder.newDir();
tempFolder.newFile();
assertThat(tmpDir).exists();
assertThat(tmpDir.list()).hasSize(2);
@Before
public void setUp() {
tempFolderProvider = new AnalysisTempFolderProvider();
projectReactor = mock(ProjectReactor.class);
ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
when(projectReactor.getRoot()).thenReturn(projectDefinition);
when(projectDefinition.getWorkDir()).thenReturn(temp.getRoot());
}

@Test
public void createTempFolder() throws IOException {
File workingDir = temp.newFolder();
File defaultDir = new File(new File(workingDir, CoreProperties.WORKING_DIRECTORY_DEFAULT_VALUE), AnalysisTempFolderProvider.TMP_NAME);
File defaultDir = new File(temp.getRoot(), AnalysisTempFolderProvider.TMP_NAME);

try {
TempFolder tempFolder = tempFolderProvider.provide(new AnalysisProperties(ImmutableMap.of("sonar.projectBaseDir", workingDir.getAbsolutePath()), ""));
tempFolder.newDir();
tempFolder.newFile();
assertThat(defaultDir).exists();
assertThat(defaultDir.list()).hasSize(2);
} finally {
FileUtils.deleteDirectory(defaultDir);
}
TempFolder tempFolder = tempFolderProvider.provide(projectReactor);
tempFolder.newDir();
tempFolder.newFile();
assertThat(defaultDir).exists();
assertThat(defaultDir.list()).hasSize(2);
}
}

+ 17
- 4
sonar-batch/src/test/java/org/sonar/batch/bootstrap/GlobalTempFolderProviderTest.java View File

@@ -55,7 +55,7 @@ public class GlobalTempFolderProviderTest {
tempFolder.newFile();
assertThat(getCreatedTempDir(workingDir)).exists();
assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);
FileUtils.deleteQuietly(workingDir);
}

@@ -73,7 +73,7 @@ public class GlobalTempFolderProviderTest {
tempFolderProvider.provide(new GlobalProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath())));
// this also checks that all other temps were deleted
assertThat(getCreatedTempDir(workingDir)).exists();
FileUtils.deleteQuietly(workingDir);
}

@@ -88,7 +88,7 @@ public class GlobalTempFolderProviderTest {
tempFolder.newFile();
assertThat(getCreatedTempDir(workingDir)).exists();
assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);
FileUtils.deleteQuietly(sonarHome);
}

@@ -97,7 +97,7 @@ public class GlobalTempFolderProviderTest {
System2 system = mock(System2.class);
tempFolderProvider = new GlobalTempFolderProvider(system);
File userHome = temp.newFolder();
when(system.envVariable("SONAR_USER_HOME")).thenReturn(null);
when(system.property("user.home")).thenReturn(userHome.getAbsolutePath().toString());

@@ -115,6 +115,19 @@ public class GlobalTempFolderProviderTest {
}
}

@Test
public void dotWorkingDir() throws IOException {
File sonarHome = temp.getRoot();
String globalWorkDir = ".";
GlobalProperties globalProperties = new GlobalProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath(),
CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkDir));

TempFolder tempFolder = tempFolderProvider.provide(globalProperties);
File newFile = tempFolder.newFile();
assertThat(newFile.getParentFile().getParentFile().getAbsolutePath()).isEqualTo(sonarHome.getAbsolutePath());
assertThat(newFile.getParentFile().getName()).startsWith(".sonartmp_");
}

private File getCreatedTempDir(File workingDir) {
assertThat(workingDir).isDirectory();
assertThat(workingDir.listFiles()).hasSize(1);

+ 0
- 79
sonar-batch/src/test/java/org/sonar/batch/bootstrap/LifecycleProviderAdapterTest.java View File

@@ -1,79 +0,0 @@
/*
* 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.bootstrap;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Before;
import org.picocontainer.Startable;
import org.junit.Test;

public class LifecycleProviderAdapterTest {
private DummyProvider provider;

@Before
public void setUp() {
provider = new DummyProvider();
provider.provide();
}

@Test
public void testStart() {
// ComponentLifecycle's start gets called on the provider
provider.start(null);
assertThat(provider.inst.started).isEqualTo(true);
assertThat(provider.isStarted()).isEqualTo(true);
assertThat(provider.inst.stopped).isEqualTo(false);
}

@Test
public void testSop() {
// ComponentLifecycle's stop gets called on the provider
provider.stop(null);
assertThat(provider.inst.stopped).isEqualTo(true);
assertThat(provider.isStarted()).isEqualTo(false);
assertThat(provider.inst.started).isEqualTo(false);
}

public class DummyProvided implements Startable {
boolean started = false;
boolean stopped = false;

@Override
public void start() {
started = true;
}

@Override
public void stop() {
stopped = true;
}
}

public class DummyProvider extends LifecycleProviderAdapter {
DummyProvided inst;

public DummyProvided provide() {
inst = new DummyProvided();
super.instance = inst;
return inst;
}
}
}

+ 2
- 2
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesIssuesModeMediumTest.java View File

@@ -86,8 +86,8 @@ public class IssuesIssuesModeMediumTest {
.property(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES)
.start();

assertThat(result2.trackedIssues()).hasSize(28);
assertThat(issueListener.issueList).hasSize(28);
assertThat(result2.trackedIssues()).hasSize(14);
assertThat(issueListener.issueList).hasSize(14);
}

private class IssueRecorder implements IssueListener {

+ 83
- 0
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java View File

@@ -0,0 +1,83 @@
/*
* 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.scan;

import org.junit.rules.ExpectedException;

import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;

import java.nio.file.Files;
import java.nio.file.Path;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;

public class ProjectLockTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

@Rule
public ExpectedException exception = ExpectedException.none();
private ProjectLock lock;

@Before
public void setUp() {
ProjectReactor projectReactor = mock(ProjectReactor.class);
ProjectDefinition projectDefinition = mock(ProjectDefinition.class);
when(projectReactor.getRoot()).thenReturn(projectDefinition);
when(projectDefinition.getBaseDir()).thenReturn(tempFolder.getRoot());

lock = new ProjectLock(projectReactor);
}

@Test
public void tryLock() {
Path lockFilePath = tempFolder.getRoot().toPath().resolve(ProjectLock.LOCK_FILE_NAME);
lock.tryLock();
assertThat(Files.exists(lockFilePath)).isTrue();
assertThat(Files.isRegularFile(lockFilePath)).isTrue();

lock.stop();
assertThat(Files.exists(lockFilePath)).isFalse();
}

@Test
public void tryLockConcurrently() {
exception.expect(IllegalStateException.class);
exception.expectMessage("Another SonarQube analysis is already in progress for this project");
lock.tryLock();
lock.tryLock();
}

@Test
public void tryLockTwice() {
lock.tryLock();
lock.stop();
lock.tryLock();
lock.stop();
}

}

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View File

@@ -483,7 +483,7 @@ public interface CoreProperties {
* @since 5.2
*/
String GLOBAL_WORKING_DIRECTORY = "sonar.globalWorking.directory";
String GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE = ".";
String GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE = "";

/**
* @since 4.2

Loading…
Cancel
Save