From fae90f2a020e9dd9cff5d8faffbdc17185c70f92 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Fri, 9 Oct 2015 10:28:47 +0200 Subject: [PATCH] Improve decoupling and coverage --- .../runner/impl/IsolatedClassloaderTest.java | 68 ++++++++++- .../runner/impl/SimulatedLauncherTest.java | 25 ++++ .../org/sonar/runner/batch/package-info.java | 20 ++++ .../org/sonar/batch/bootstrapper/Batch.java | 10 +- .../org/sonar/runner/batch/BatchFactory.java | 30 +++++ .../runner/batch/BatchIsolatedLauncher.java | 33 ++---- .../runner/batch/DefaultBatchFactory.java | 49 ++++++++ .../org/sonar/runner/batch/package-info.java | 20 ++++ .../batch/BatchIsolatedLauncherTest.java | 107 ++++++++++++++++++ ...Test.java => DefaultBatchFactoryTest.java} | 8 +- 10 files changed, 334 insertions(+), 36 deletions(-) create mode 100644 sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/package-info.java create mode 100644 sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchFactory.java create mode 100644 sonar-runner-batch/src/main/java/org/sonar/runner/batch/DefaultBatchFactory.java create mode 100644 sonar-runner-batch/src/main/java/org/sonar/runner/batch/package-info.java create mode 100644 sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java rename sonar-runner-batch/src/test/java/org/sonar/runner/batch/{IsolatedLauncherTest.java => DefaultBatchFactoryTest.java} (87%) diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java index b0a9276..3c7b2b8 100644 --- a/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java +++ b/sonar-runner-api/src/test/java/org/sonar/runner/impl/IsolatedClassloaderTest.java @@ -19,9 +19,18 @@ */ package org.sonar.runner.impl; +import org.junit.Before; + +import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.Enumeration; import java.util.HashSet; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -31,16 +40,71 @@ public class IsolatedClassloaderTest { @Rule public ExpectedException thrown = ExpectedException.none(); + private IsolatedClassloader classLoader; + + @Before + public void setUp() { + ClassLoader parent = getClass().getClassLoader(); + classLoader = new IsolatedClassloader(parent, new ClassloadRules(new HashSet(), new HashSet())); + } + @Test public void should_use_isolated_system_classloader_when_parent_is_excluded() throws ClassNotFoundException, IOException { thrown.expect(ClassNotFoundException.class); thrown.expectMessage("org.junit.Test"); - ClassLoader parent = getClass().getClassLoader(); - IsolatedClassloader classLoader = new IsolatedClassloader(parent, new ClassloadRules(new HashSet(), new HashSet())); // JUnit is available in the parent classloader (classpath used to execute this test) but not in the core JVM assertThat(classLoader.loadClass("java.lang.String", false)).isNotNull(); classLoader.loadClass("org.junit.Test", false); classLoader.close(); } + + @Test + public void should_use_parent_to_load() throws ClassNotFoundException, IOException { + ClassloadRules rules = mock(ClassloadRules.class); + when(rules.canLoad("org.junit.Test")).thenReturn(true); + classLoader = new IsolatedClassloader(getClass().getClassLoader(), rules); + assertThat(classLoader.loadClass("org.junit.Test", false)).isNotNull(); + } + + @Test + public void add_jars() throws MalformedURLException { + File f = new File("dummy"); + File[] files = {f}; + classLoader.addFiles(Arrays.asList(files)); + + assertThat(classLoader.getURLs()).contains(f.toURI().toURL()); + } + + @Test + public void error_add_jars() { + File f = mock(File.class); + when(f.toURI()).thenThrow(MalformedURLException.class); + File[] files = {f}; + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("Fail to create classloader"); + + classLoader.addFiles(Arrays.asList(files)); + } + + @Test + public void dont_get_resource_from_parent() { + URL resource2 = classLoader.getParent().getResource("fake.jar"); + assertThat(resource2).isNotNull(); + + // should not find resource through parent classloader + URL resource = classLoader.getResource("fake.jar"); + assertThat(resource).isNull(); + } + + @Test + public void dont_get_resources_from_parent() throws IOException { + Enumeration resource2 = classLoader.getParent().getResources("fake.jar"); + assertThat(resource2.hasMoreElements()).isTrue(); + + // should not find resource through parent classloader + Enumeration resource = classLoader.getResources("fake.jar"); + assertThat(resource.hasMoreElements()).isFalse(); + } } diff --git a/sonar-runner-api/src/test/java/org/sonar/runner/impl/SimulatedLauncherTest.java b/sonar-runner-api/src/test/java/org/sonar/runner/impl/SimulatedLauncherTest.java index ae4101b..37f1e9f 100644 --- a/sonar-runner-api/src/test/java/org/sonar/runner/impl/SimulatedLauncherTest.java +++ b/sonar-runner-api/src/test/java/org/sonar/runner/impl/SimulatedLauncherTest.java @@ -19,6 +19,7 @@ */ package org.sonar.runner.impl; +import org.sonar.runner.batch.IssueListener; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -28,8 +29,13 @@ import org.sonar.runner.cache.Logger; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.Properties; +import static org.mockito.Mockito.doThrow; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -61,11 +67,30 @@ public class SimulatedLauncherTest { assertDump(global, analysis); } + @Test + public void testDump_with_issue_listener() throws IOException { + Properties global = new Properties(); + global.putAll(createProperties(true)); + Properties analysis = new Properties(); + analysis.putAll(createProperties(false)); + + launcher.start(global, null, false); + launcher.execute(analysis, mock(IssueListener.class)); + assertDump(global, analysis); + } + @Test(expected = IllegalStateException.class) public void error_if_no_dump_file() { launcher.execute(new Properties()); } + @Test(expected = IllegalStateException.class) + public void error_dump() throws IOException { + Properties p = mock(Properties.class); + doThrow(IOException.class).when(p).store(any(OutputStream.class), anyString()); + launcher.execute(p); + } + @Test public void no_ops() { launcher.syncProject(null); diff --git a/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/package-info.java b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/package-info.java new file mode 100644 index 0000000..97bc469 --- /dev/null +++ b/sonar-runner-batch-interface/src/main/java/org/sonar/runner/batch/package-info.java @@ -0,0 +1,20 @@ +/* + * SonarQube Runner - Batch Interface + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.runner.batch; diff --git a/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java index 60f1465..e306ea2 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java +++ b/sonar-runner-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java @@ -28,7 +28,7 @@ import org.picocontainer.annotations.Nullable; * * @since 2.14 */ -public final class Batch { +public class Batch { private Batch(Builder builder) { } @@ -52,7 +52,7 @@ public final class Batch { return start(false); } - public synchronized Batch start(boolean forceSync) { + public synchronized Batch start(boolean preferCache) { return this; } @@ -83,12 +83,6 @@ public final class Batch { public synchronized void stop() { } - private void doStop(boolean swallowException) { - } - - private void configureLogging() { - } - public static Builder builder() { return new Builder(); } diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchFactory.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchFactory.java new file mode 100644 index 0000000..e899ba0 --- /dev/null +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchFactory.java @@ -0,0 +1,30 @@ +/* + * SonarQube Runner - Batch + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.runner.batch; + +import org.picocontainer.annotations.Nullable; +import org.sonar.batch.bootstrapper.Batch; + +import java.util.List; +import java.util.Properties; + +interface BatchFactory { + Batch createBatch(Properties properties, @Nullable final org.sonar.runner.batch.LogOutput logOutput, @Nullable List extensions); +} diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java index b7a49f3..1ac82f1 100644 --- a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/BatchIsolatedLauncher.java @@ -28,9 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.picocontainer.annotations.Nullable; import org.sonar.batch.bootstrapper.Batch; -import org.sonar.batch.bootstrapper.EnvironmentInformation; /** * This class is executed within the classloader provided by the server. It contains the installed plugins and @@ -38,10 +36,19 @@ import org.sonar.batch.bootstrapper.EnvironmentInformation; */ public class BatchIsolatedLauncher implements IsolatedLauncher { private Batch batch = null; + private BatchFactory factory = null; + + public BatchIsolatedLauncher() { + this.factory = new DefaultBatchFactory(); + } + + public BatchIsolatedLauncher(BatchFactory factory) { + this.factory = factory; + } @Override public void start(Properties globalProperties, org.sonar.runner.batch.LogOutput logOutput, boolean preferCache) { - batch = createBatch(globalProperties, logOutput, null); + batch = factory.createBatch(globalProperties, logOutput, null); batch.start(preferCache); } @@ -66,30 +73,12 @@ public class BatchIsolatedLauncher implements IsolatedLauncher { batch.syncProject(projectKey); } - Batch createBatch(Properties properties, @Nullable final org.sonar.runner.batch.LogOutput logOutput, @Nullable List extensions) { - EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.app"), properties.getProperty("sonarRunner.appVersion")); - Batch.Builder builder = Batch.builder() - .setEnvironment(env) - .setBootstrapProperties((Map) properties); - - if (extensions != null) { - builder.addComponents(extensions); - } - - if (logOutput != null) { - // Do that is a separate class to avoid NoClassDefFoundError for org/sonar/batch/bootstrapper/LogOutput - Compatibility.setLogOutputFor5dot2(builder, logOutput); - } - - return builder.build(); - } - /** * This method exists for backward compatibility with SonarQube < 5.2. */ @Override public void executeOldVersion(Properties properties, List extensions) { - createBatch(properties, null, extensions).execute(); + factory.createBatch(properties, null, extensions).execute(); } @Override diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/DefaultBatchFactory.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/DefaultBatchFactory.java new file mode 100644 index 0000000..e7d1a06 --- /dev/null +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/DefaultBatchFactory.java @@ -0,0 +1,49 @@ +/* + * SonarQube Runner - Batch + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.runner.batch; + +import org.picocontainer.annotations.Nullable; +import org.sonar.batch.bootstrapper.Batch; +import org.sonar.batch.bootstrapper.EnvironmentInformation; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +class DefaultBatchFactory implements BatchFactory { + @Override + public Batch createBatch(Properties properties, @Nullable final org.sonar.runner.batch.LogOutput logOutput, @Nullable List extensions) { + EnvironmentInformation env = new EnvironmentInformation(properties.getProperty("sonarRunner.app"), properties.getProperty("sonarRunner.appVersion")); + Batch.Builder builder = Batch.builder() + .setEnvironment(env) + .setBootstrapProperties((Map) properties); + + if (extensions != null) { + builder.addComponents(extensions); + } + + if (logOutput != null) { + // Do that is a separate class to avoid NoClassDefFoundError for org/sonar/batch/bootstrapper/LogOutput + Compatibility.setLogOutputFor5dot2(builder, logOutput); + } + + return builder.build(); + } +} diff --git a/sonar-runner-batch/src/main/java/org/sonar/runner/batch/package-info.java b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/package-info.java new file mode 100644 index 0000000..525879d --- /dev/null +++ b/sonar-runner-batch/src/main/java/org/sonar/runner/batch/package-info.java @@ -0,0 +1,20 @@ +/* + * SonarQube Runner - Batch + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.runner.batch; diff --git a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java new file mode 100644 index 0000000..d222a6d --- /dev/null +++ b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/BatchIsolatedLauncherTest.java @@ -0,0 +1,107 @@ +/* + * SonarQube Runner - Batch + * Copyright (C) 2011 SonarSource + * sonarqube@googlegroups.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.runner.batch; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.batch.bootstrapper.Batch; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.mockito.Matchers.eq; + +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Mockito.mock; + +public class BatchIsolatedLauncherTest { + private Batch batch; + private BatchFactory factory; + private BatchIsolatedLauncher launcher; + + @Before + public void setUp() { + factory = mock(BatchFactory.class); + batch = mock(Batch.class); + when(factory.createBatch(any(Properties.class), any(LogOutput.class), anyListOf(Object.class))).thenReturn(batch); + launcher = new BatchIsolatedLauncher(factory); + } + + @Test + public void executeOld() { + Properties prop = new Properties(); + List list = new LinkedList<>(); + + launcher.executeOldVersion(prop, list); + + verify(factory).createBatch(prop, null, list); + verify(batch).execute(); + + verifyNoMoreInteractions(batch); + verifyNoMoreInteractions(factory); + } + + @Test(expected = NullPointerException.class) + public void executeWithoutStart() { + IssueListener issueListener = mock(IssueListener.class); + Properties prop = new Properties(); + launcher.execute(prop, issueListener); + } + + @Test + public void executeWithListener() { + IssueListener issueListener = mock(IssueListener.class); + Properties prop = new Properties(); + + launcher.start(null, null, true); + launcher.execute(prop, issueListener); + + verify(batch).start(true); + verify(batch).executeTask(eq((Map) prop), any(org.sonar.batch.bootstrapper.IssueListener.class)); + + verifyNoMoreInteractions(batch); + } + + @Test + public void proxy() { + Properties prop = new Properties(); + + launcher.start(prop, null, true); + launcher.syncProject("proj"); + launcher.execute(prop); + launcher.stop(); + + verify(factory).createBatch(any(Properties.class), any(LogOutput.class), anyListOf(Object.class)); + verify(batch).start(true); + verify(batch).syncProject("proj"); + verify(batch).executeTask((Map) prop); + verify(batch).stop(); + + verifyNoMoreInteractions(batch); + verifyNoMoreInteractions(factory); + } + +} diff --git a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/IsolatedLauncherTest.java b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/DefaultBatchFactoryTest.java similarity index 87% rename from sonar-runner-batch/src/test/java/org/sonar/runner/batch/IsolatedLauncherTest.java rename to sonar-runner-batch/src/test/java/org/sonar/runner/batch/DefaultBatchFactoryTest.java index 7ea9227..4574940 100644 --- a/sonar-runner-batch/src/test/java/org/sonar/runner/batch/IsolatedLauncherTest.java +++ b/sonar-runner-batch/src/test/java/org/sonar/runner/batch/DefaultBatchFactoryTest.java @@ -25,10 +25,10 @@ import org.sonar.batch.bootstrapper.Batch; import static org.fest.assertions.Assertions.assertThat; -public class IsolatedLauncherTest { +public class DefaultBatchFactoryTest { - Properties props = new Properties(); - BatchIsolatedLauncher launcher = new BatchIsolatedLauncher(); + private Properties props = new Properties(); + private BatchFactory factory = new DefaultBatchFactory(); @Test public void should_create_batch() { @@ -37,7 +37,7 @@ public class IsolatedLauncherTest { props.setProperty("sonar.projectName", "Sample"); props.setProperty("sonar.projectVersion", "1.0"); props.setProperty("sonar.sources", "src"); - Batch batch = launcher.createBatch(props, null, null); + Batch batch = factory.createBatch(props, null, null); assertThat(batch).isNotNull(); } -- 2.39.5