From 7576290087d9ea9f59033dcf2c45474371cea1b9 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Tue, 19 Dec 2023 14:09:12 +0100 Subject: [PATCH 01/62] don't create coverage and logs directory on install since we now write coverage and logs to the temp dir --- .../installer/steps/InstallAgentFilesStep.java | 18 ------------------ .../profiler/installer/InstallerTest.java | 10 ---------- 2 files changed, 28 deletions(-) diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallAgentFilesStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallAgentFilesStep.java index ce33119b9..b985cbbac 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallAgentFilesStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallAgentFilesStep.java @@ -26,14 +26,6 @@ public InstallAgentFilesStep(Path sourceDirectory, Path installDirectory) { this.installDirectory = installDirectory; } - private Path getCoverageDirectory() { - return installDirectory.resolve("coverage"); - } - - private Path getLogDirectory() { - return installDirectory.resolve("logs"); - } - private Path getTeamscalePropertiesPath() { return installDirectory.resolve("teamscale.properties"); } @@ -44,7 +36,6 @@ public void install(TeamscaleCredentials credentials) throws FatalInstallerError createAgentDirectory(); copyAgentFiles(); writeTeamscaleProperties(credentials); - makeCoverageAndLogDirectoriesWorldWritable(); makeAllProfilerFilesWorldReadable(); } @@ -82,15 +73,6 @@ private void makeAllProfilerFilesWorldReadable() throws FatalInstallerError { } } - private void makeCoverageAndLogDirectoriesWorldWritable() throws FatalInstallerError { - InstallFileUtils.createDirectory(getCoverageDirectory()); - InstallFileUtils.makeWritable(getCoverageDirectory()); - - InstallFileUtils.createDirectory(getLogDirectory()); - InstallFileUtils.makeWritable(getLogDirectory()); - - } - private void writeTeamscaleProperties(TeamscaleCredentials credentials) throws FatalInstallerError { Properties properties = new Properties(); properties.setProperty("url", credentials.url.toString()); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java index 56a672746..6668bafdd 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java @@ -18,7 +18,6 @@ import java.util.Properties; import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ; -import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -39,8 +38,6 @@ class InstallerTest { private Path installedFile; private Path installedNestedFile; private Path installedTeamscaleProperties; - private Path installedCoverageDirectory; - private Path installedLogsDirectory; private Path installedAgentLibrary; private Path environmentFile; @@ -73,8 +70,6 @@ void setUpSourceDirectory() throws IOException { installedFile = targetDirectory.resolve(sourceDirectory.relativize(fileToInstall)); installedNestedFile = targetDirectory.resolve(sourceDirectory.relativize(nestedFileToInstall)); installedTeamscaleProperties = targetDirectory.resolve("teamscale.properties"); - installedCoverageDirectory = targetDirectory.resolve("coverage"); - installedLogsDirectory = targetDirectory.resolve("logs"); installedAgentLibrary = targetDirectory.resolve("lib/teamscale-jacoco-agent.jar"); } @@ -102,14 +97,9 @@ void successfulInstallation() throws FatalInstallerError, IOException { assertThat(properties.getProperty("username")).isEqualTo("user"); assertThat(properties.getProperty("accesskey")).isEqualTo("accesskey"); - assertThat(installedCoverageDirectory).exists().isReadable().isWritable(); - assertThat(installedLogsDirectory).exists().isReadable().isWritable(); - if (SystemUtils.isLinux()) { assertThat(Files.getPosixFilePermissions(installedTeamscaleProperties)).contains(OTHERS_READ); assertThat(Files.getPosixFilePermissions(installedFile)).contains(OTHERS_READ); - assertThat(Files.getPosixFilePermissions(installedLogsDirectory)).contains(OTHERS_READ, OTHERS_WRITE); - assertThat(Files.getPosixFilePermissions(installedCoverageDirectory)).contains(OTHERS_READ, OTHERS_WRITE); assertThat(environmentFile).content().isEqualTo(ENVIRONMENT_CONTENT + "\nJAVA_TOOL_OPTIONS=-javaagent:" + installedAgentLibrary From 06385085f7c3cd2a3d86126f56c1097b2815747b Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Tue, 19 Dec 2023 14:09:34 +0100 Subject: [PATCH 02/62] rename linux tests --- .../installer/{InstallerTest.java => LinuxInstallerTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename installer/src/test/java/com/teamscale/profiler/installer/{InstallerTest.java => LinuxInstallerTest.java} (99%) diff --git a/installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java similarity index 99% rename from installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java rename to installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java index 6668bafdd..0348c46c6 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/InstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java @@ -22,7 +22,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; @EnabledOnOs(OS.LINUX) -class InstallerTest { +class LinuxInstallerTest { private static final int TEAMSCALE_PORT = 8059; private static final String FILE_TO_INSTALL_CONTENT = "install-me"; From ee68cc5ca9b6a0a10a2f01589eb10ccd8bf79cfb Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Tue, 19 Dec 2023 16:52:16 +0100 Subject: [PATCH 03/62] implement windows environment variable step --- installer/build.gradle.kts | 1 + .../profiler/installer/EnvironmentMap.java | 4 + .../profiler/installer/Installer.java | 28 ++++-- .../profiler/installer/steps/IStep.java | 4 + .../steps/InstallEtcEnvironmentStep.java | 13 +-- .../installer/steps/InstallSystemdStep.java | 13 +-- .../InstallWindowsSystemEnvironmentStep.java | 85 ++++++++++++++++ .../profiler/installer/windows/IRegistry.java | 18 ++++ .../installer/windows/WindowsRegistry.java | 48 +++++++++ ...stallWindowsSystemEnvironmentStepTest.java | 99 +++++++++++++++++++ 10 files changed, 291 insertions(+), 22 deletions(-) create mode 100644 installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java create mode 100644 installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java create mode 100644 installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java diff --git a/installer/build.gradle.kts b/installer/build.gradle.kts index abf8b4e87..94a4cf718 100644 --- a/installer/build.gradle.kts +++ b/installer/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { implementation(libs.teamscaleLibCommons) implementation(libs.picocli.core) annotationProcessor(libs.picocli.codegen) + implementation("net.java.dev.jna:jna-platform:5.14.0") testImplementation(libs.spark) } diff --git a/installer/src/main/java/com/teamscale/profiler/installer/EnvironmentMap.java b/installer/src/main/java/com/teamscale/profiler/installer/EnvironmentMap.java index af4c334ef..2069cbe8a 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/EnvironmentMap.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/EnvironmentMap.java @@ -20,6 +20,10 @@ public EnvironmentMap(String... keysAndValues) { } } + public Map getMap() { + return environment; + } + private String escape(String value) { return value.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\""); } diff --git a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java index ce48ffc8b..ae0a2d10f 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java @@ -4,6 +4,8 @@ import com.teamscale.profiler.installer.steps.InstallAgentFilesStep; import com.teamscale.profiler.installer.steps.InstallEtcEnvironmentStep; import com.teamscale.profiler.installer.steps.InstallSystemdStep; +import com.teamscale.profiler.installer.steps.InstallWindowsSystemEnvironmentStep; +import com.teamscale.profiler.installer.windows.WindowsRegistry; import org.conqat.lib.commons.collections.CollectionUtils; import org.conqat.lib.commons.system.SystemUtils; import picocli.CommandLine; @@ -18,20 +20,26 @@ /** Installs the agent system-globally. */ public class Installer { - private static final Path DEFAULT_INSTALL_DIRECTORY = Paths.get("/opt/teamscale-profiler/java"); + private static final Path DEFAULT_INSTALL_DIRECTORY = windowsOrLinux( + Paths.get(System.getenv("ProgramFiles")), + Paths.get("/opt/teamscale-profiler/java") + ); + private static final Path DEFAULT_ETC_DIRECTORY = Paths.get("/etc"); - private static final String RERUN_ADVICE = getRerunAdvice(); + private static final String RERUN_ADVICE = windowsOrLinux( + "Try running this installer as Administrator.", + "Try running this installer as root, e.g. with sudo." + ); - private static String getRerunAdvice() { + private static T windowsOrLinux(T windowsValue, T linuxValue) { if (SystemUtils.isWindows()) { - return "Try running this installer as Administrator."; + return windowsValue; } else { - return "Try running this installer as root, e.g. with sudo."; + return linuxValue; } } - /** Returns the directory that contains the agent to install or null if it can't be resolved. */ private static Path getDefaultSourceDirectory() { try { @@ -61,6 +69,7 @@ private static Path getDefaultSourceDirectory() { public Installer(Path sourceDirectory, Path installDirectory, Path etcDirectory, boolean reloadSystemdDaemon) { EnvironmentMap environmentVariables = getEnvironmentVariables(installDirectory); this.steps = Arrays.asList(new InstallAgentFilesStep(sourceDirectory, installDirectory), + new InstallWindowsSystemEnvironmentStep(environmentVariables, new WindowsRegistry()), new InstallEtcEnvironmentStep(etcDirectory, environmentVariables), new InstallSystemdStep(etcDirectory, environmentVariables, reloadSystemdDaemon)); } @@ -130,6 +139,9 @@ public static int uninstall() { public void runInstall(TeamscaleCredentials credentials) throws FatalInstallerError { TeamscaleUtils.checkTeamscaleConnection(credentials); for (IStep step : steps) { + if (!step.shouldRun()) { + continue; + } step.install(credentials); } } @@ -141,6 +153,10 @@ public void runInstall(TeamscaleCredentials credentials) throws FatalInstallerEr public UninstallerErrorReporter runUninstall() { UninstallerErrorReporter errorReporter = new UninstallerErrorReporter(); for (IStep step : CollectionUtils.reverse(steps)) { + if (!step.shouldRun()) { + continue; + } + step.uninstall(errorReporter); if (errorReporter.errorsReported) { break; diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java index 1cb2fa4b1..6050e6c93 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java @@ -20,6 +20,10 @@ public interface IStep { */ void uninstall(IUninstallErrorReporter errorReporter); + default boolean shouldRun() { + return true; + } + /** * Used to report errors that happen during uninstallation. During uninstalling, we want to remove everything we can * remove, even if some parts of the process fail. Thus we don't just throw exceptions and abort. diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java index 7c4e3ad0c..e5447c446 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java @@ -29,11 +29,12 @@ public InstallEtcEnvironmentStep(Path etcDirectory, EnvironmentMap environmentMa } @Override - public void install(TeamscaleCredentials credentials) throws FatalInstallerError { - if (!SystemUtils.isLinux()) { - return; - } + public boolean shouldRun() { + return SystemUtils.isLinux(); + } + @Override + public void install(TeamscaleCredentials credentials) throws FatalInstallerError { Path environmentFile = getEnvironmentFile(); if (!Files.exists(environmentFile)) { System.err.println( @@ -60,10 +61,6 @@ private Path getEnvironmentFile() { @Override public void uninstall(IUninstallErrorReporter errorReporter) { - if (!SystemUtils.isLinux()) { - return; - } - Path environmentFile = getEnvironmentFile(); if (!Files.exists(environmentFile)) { return; diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java index 07c758bf4..08752b625 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java @@ -30,11 +30,12 @@ public InstallSystemdStep(Path etcDirectory, EnvironmentMap environmentMap, bool } @Override - public void install(TeamscaleCredentials credentials) throws FatalInstallerError { - if (!SystemUtils.isLinux()) { - return; - } + public boolean shouldRun() { + return SystemUtils.isLinux(); + } + @Override + public void install(TeamscaleCredentials credentials) throws FatalInstallerError { if (!Files.exists(getSystemdEtcDirectory())) { System.out.println("systemd could not be detected. Not installing profiler for systemd services."); // system has no systemd installed @@ -111,10 +112,6 @@ private Path getSystemdConfigFile() { @Override public void uninstall(IUninstallErrorReporter errorReporter) { - if (!SystemUtils.isLinux()) { - return; - } - Path systemdConfigFile = getSystemdConfigFile(); if (!Files.exists(systemdConfigFile)) { return; diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java new file mode 100644 index 000000000..daf5ddc66 --- /dev/null +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java @@ -0,0 +1,85 @@ +package com.teamscale.profiler.installer.steps; + +import com.teamscale.profiler.installer.EnvironmentMap; +import com.teamscale.profiler.installer.FatalInstallerError; +import com.teamscale.profiler.installer.TeamscaleCredentials; +import com.teamscale.profiler.installer.windows.IRegistry; +import org.conqat.lib.commons.string.StringUtils; +import org.conqat.lib.commons.system.SystemUtils; + +import java.util.Map; + +/** On Linux, registers the agent globally via environment variables set in /etc/environment */ +public class InstallWindowsSystemEnvironmentStep implements IStep { + + private final EnvironmentMap environmentVariables; + private final IRegistry registry; + + public static final String ENVIRONMENT_REGISTRY_KEY = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; + + public InstallWindowsSystemEnvironmentStep(EnvironmentMap environmentMap, IRegistry registry) { + this.environmentVariables = environmentMap; + this.registry = registry; + } + + @Override + public boolean shouldRun() { + return SystemUtils.isWindows(); + } + + @Override + public void install(TeamscaleCredentials credentials) throws FatalInstallerError { + Map map = environmentVariables.getMap(); + for (String variable : map.keySet()) { + addProfiler(variable, map.get(variable), registry); + } + } + + @Override + public void uninstall(IUninstallErrorReporter errorReporter) { + Map map = environmentVariables.getMap(); + for (String variable : map.keySet()) { + try { + String valueToRemove = map.get(variable); + removeProfiler(variable, valueToRemove, registry); + } catch (FatalInstallerError e) { + errorReporter.report(e); + } + } + } + + /*package*/ + static void addProfiler(String variable, String valueToAdd, IRegistry registry) throws FatalInstallerError { + String currentValue = registry.getHklmValue(ENVIRONMENT_REGISTRY_KEY, variable); + + String newValue = valueToAdd; + if (!StringUtils.isEmpty(currentValue)) { + newValue = valueToAdd + " " + currentValue; + } + registry.setHklmValue(ENVIRONMENT_REGISTRY_KEY, variable, newValue); + } + + /*package*/ + static void removeProfiler(String variable, String valueToRemove, + IRegistry registry) throws FatalInstallerError { + String currentValue = registry.getHklmValue(ENVIRONMENT_REGISTRY_KEY, variable); + if (StringUtils.isEmpty(currentValue)) { + return; + } + + if (currentValue.equals(valueToRemove)) { + registry.deleteHklmValue(ENVIRONMENT_REGISTRY_KEY, variable); + return; + } + + if (!currentValue.contains(valueToRemove)) { + return; + } + + if (currentValue.contains(valueToRemove + " ")) { + valueToRemove += " "; + } + String newValue = currentValue.replace(valueToRemove, ""); + registry.setHklmValue(ENVIRONMENT_REGISTRY_KEY, variable, newValue); + } +} diff --git a/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java b/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java new file mode 100644 index 000000000..d830a0b93 --- /dev/null +++ b/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java @@ -0,0 +1,18 @@ +package com.teamscale.profiler.installer.windows; + +import com.teamscale.profiler.installer.FatalInstallerError; + +/** + * Abstraction of the Windows registry to make registry usage testable. + */ +public interface IRegistry { + + /** + * Reads a registry value inside a registry key in HKLM. + * Returns null if the value does not exist. + */ + String getHklmValue(String key, String name) throws FatalInstallerError; + void setHklmValue(String key, String name, String value) throws FatalInstallerError; + void deleteHklmValue(String key, String name) throws FatalInstallerError; + +} diff --git a/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java b/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java new file mode 100644 index 000000000..bc7ef4c96 --- /dev/null +++ b/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java @@ -0,0 +1,48 @@ +package com.teamscale.profiler.installer.windows; + +import com.sun.jna.platform.win32.Advapi32Util; +import com.sun.jna.platform.win32.Win32Exception; +import com.sun.jna.platform.win32.WinReg; +import com.teamscale.profiler.installer.FatalInstallerError; + +/** + * Accesses the Windows registry. + */ +public class WindowsRegistry implements IRegistry { + + @Override + public String getHklmValue(String key, String name) throws FatalInstallerError { + try { + if (!Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, key, name)) { + return null; + } + return Advapi32Util.registryGetStringValue(WinReg.HKEY_LOCAL_MACHINE, key, name); + } catch (Win32Exception e) { + throw new FatalInstallerError( + "Failed to read registry key HKLM\\" + key + ". Try running this installer as Administrator.", e); + } + } + + @Override + public void setHklmValue(String key, String name, String value) throws FatalInstallerError { + try { + Advapi32Util.registrySetStringValue(WinReg.HKEY_LOCAL_MACHINE, key, name, value); + } catch (Win32Exception e) { + throw new FatalInstallerError( + "Failed to write registry key HKLM\\" + key + ". Try running this installer as Administrator.", e); + } + } + + @Override + public void deleteHklmValue(String key, String name) throws FatalInstallerError { + try { + if (!Advapi32Util.registryValueExists(WinReg.HKEY_LOCAL_MACHINE, key, name)) { + return; + } + Advapi32Util.registryDeleteValue(WinReg.HKEY_LOCAL_MACHINE, key, name); + } catch (Win32Exception e) { + throw new FatalInstallerError( + "Failed to delete registry key HKLM\\" + key + ". Try running this installer as Administrator.", e); + } + } +} diff --git a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java new file mode 100644 index 000000000..9a45b270b --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java @@ -0,0 +1,99 @@ +package com.teamscale.profiler.installer.steps; + +import com.teamscale.profiler.installer.EnvironmentMap; +import com.teamscale.profiler.installer.FatalInstallerError; +import com.teamscale.profiler.installer.Installer; +import com.teamscale.profiler.installer.TeamscaleCredentials; +import com.teamscale.profiler.installer.windows.IRegistry; +import okhttp3.HttpUrl; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class InstallWindowsSystemEnvironmentStepTest { + + private static final TeamscaleCredentials CREDENTIALS = new TeamscaleCredentials( + HttpUrl.get("http://localhost:" + 8058 + "/"), "user", "accesskey"); + + private static final EnvironmentMap environment = new EnvironmentMap("_JAVA_OPTIONS", + "\"-javaagent:C:\\Program Files\\foo.jar\""); + + private static class FakeRegistry implements IRegistry { + + private final Map values = new HashMap<>(); + + @Override + public String getHklmValue(String key, String name) { + return values.get(key + "\\" + name); + } + + @Override + public void setHklmValue(String key, String name, String value) { + values.put(key + "\\" + name, value); + } + + @Override + public void deleteHklmValue(String key, String name) { + values.remove(key + "\\" + name); + } + + public String getVariable(String name) { + return getHklmValue(InstallWindowsSystemEnvironmentStep.ENVIRONMENT_REGISTRY_KEY, name); + } + + public void setVariable(String name, String value) { + setHklmValue(InstallWindowsSystemEnvironmentStep.ENVIRONMENT_REGISTRY_KEY, name, value); + } + } + + @Test + void successfulInstall() throws FatalInstallerError { + FakeRegistry registry = new FakeRegistry(); + new InstallWindowsSystemEnvironmentStep(environment, registry).install(CREDENTIALS); + + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo(environment.getMap().get("_JAVA_OPTIONS")); + } + + @Test + void successfulUninstall() throws FatalInstallerError { + FakeRegistry registry = new FakeRegistry(); + Installer.UninstallerErrorReporter errorReporter = new Installer.UninstallerErrorReporter(); + + new InstallWindowsSystemEnvironmentStep(environment, registry).install(CREDENTIALS); + new InstallWindowsSystemEnvironmentStep(environment, registry).uninstall(errorReporter); + + assertThat(errorReporter.wereErrorsReported()).isFalse(); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isNull(); + } + + @Test + void addAndRemoveProfiler() throws FatalInstallerError { + FakeRegistry registry = new FakeRegistry(); + + InstallWindowsSystemEnvironmentStep.addProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo("-javaagent:foo.jar"); + + InstallWindowsSystemEnvironmentStep.removeProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isNullOrEmpty(); + } + + @Test + void addAndRemoveProfilerWithPreviousValue() throws FatalInstallerError { + FakeRegistry registry = new FakeRegistry(); + + registry.setVariable("_JAVA_OPTIONS", "-javaagent:other.jar"); + InstallWindowsSystemEnvironmentStep.addProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo("-javaagent:foo.jar -javaagent:other.jar"); + + InstallWindowsSystemEnvironmentStep.removeProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo("-javaagent:other.jar"); + + // removing it again should do nothing + InstallWindowsSystemEnvironmentStep.removeProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo("-javaagent:other.jar"); + } + +} \ No newline at end of file From 49dc75c8cacb07c11bf4e85f7f9f1cb9ad78d30a Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Tue, 19 Dec 2023 17:04:30 +0100 Subject: [PATCH 04/62] add test for WindowsRegistry and document code --- .../profiler/installer/Installer.java | 2 +- .../InstallWindowsSystemEnvironmentStep.java | 23 +++++++---- .../profiler/installer/windows/IRegistry.java | 4 ++ .../installer/windows/WindowsRegistry.java | 10 +++++ .../installer/LinuxInstallerTest.java | 1 - ...stallWindowsSystemEnvironmentStepTest.java | 9 ++++- .../windows/WindowsRegistryTest.java | 40 +++++++++++++++++++ 7 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/windows/WindowsRegistryTest.java diff --git a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java index ae0a2d10f..f60041dc3 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java @@ -69,7 +69,7 @@ private static Path getDefaultSourceDirectory() { public Installer(Path sourceDirectory, Path installDirectory, Path etcDirectory, boolean reloadSystemdDaemon) { EnvironmentMap environmentVariables = getEnvironmentVariables(installDirectory); this.steps = Arrays.asList(new InstallAgentFilesStep(sourceDirectory, installDirectory), - new InstallWindowsSystemEnvironmentStep(environmentVariables, new WindowsRegistry()), + new InstallWindowsSystemEnvironmentStep(environmentVariables, WindowsRegistry.INSTANCE), new InstallEtcEnvironmentStep(etcDirectory, environmentVariables), new InstallSystemdStep(etcDirectory, environmentVariables, reloadSystemdDaemon)); } diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java index daf5ddc66..f4d50e936 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java @@ -4,19 +4,18 @@ import com.teamscale.profiler.installer.FatalInstallerError; import com.teamscale.profiler.installer.TeamscaleCredentials; import com.teamscale.profiler.installer.windows.IRegistry; +import com.teamscale.profiler.installer.windows.WindowsRegistry; import org.conqat.lib.commons.string.StringUtils; import org.conqat.lib.commons.system.SystemUtils; import java.util.Map; -/** On Linux, registers the agent globally via environment variables set in /etc/environment */ +/** On Windows, registers the agent globally via environment variables set for the entire machine. */ public class InstallWindowsSystemEnvironmentStep implements IStep { private final EnvironmentMap environmentVariables; private final IRegistry registry; - public static final String ENVIRONMENT_REGISTRY_KEY = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; - public InstallWindowsSystemEnvironmentStep(EnvironmentMap environmentMap, IRegistry registry) { this.environmentVariables = environmentMap; this.registry = registry; @@ -48,27 +47,35 @@ public void uninstall(IUninstallErrorReporter errorReporter) { } } + /** + * Adds the profiler to the given registry under the given variable, appending it in case the variable already has a + * value set. + */ /*package*/ static void addProfiler(String variable, String valueToAdd, IRegistry registry) throws FatalInstallerError { - String currentValue = registry.getHklmValue(ENVIRONMENT_REGISTRY_KEY, variable); + String currentValue = registry.getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, variable); String newValue = valueToAdd; if (!StringUtils.isEmpty(currentValue)) { newValue = valueToAdd + " " + currentValue; } - registry.setHklmValue(ENVIRONMENT_REGISTRY_KEY, variable, newValue); + registry.setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, variable, newValue); } + /** + * Removes the profiler from the given registry under the given variable, leaving any other parts of the variable in + * place. + */ /*package*/ static void removeProfiler(String variable, String valueToRemove, IRegistry registry) throws FatalInstallerError { - String currentValue = registry.getHklmValue(ENVIRONMENT_REGISTRY_KEY, variable); + String currentValue = registry.getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, variable); if (StringUtils.isEmpty(currentValue)) { return; } if (currentValue.equals(valueToRemove)) { - registry.deleteHklmValue(ENVIRONMENT_REGISTRY_KEY, variable); + registry.deleteHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, variable); return; } @@ -80,6 +87,6 @@ static void removeProfiler(String variable, String valueToRemove, valueToRemove += " "; } String newValue = currentValue.replace(valueToRemove, ""); - registry.setHklmValue(ENVIRONMENT_REGISTRY_KEY, variable, newValue); + registry.setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, variable, newValue); } } diff --git a/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java b/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java index d830a0b93..502870cfb 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/windows/IRegistry.java @@ -12,7 +12,11 @@ public interface IRegistry { * Returns null if the value does not exist. */ String getHklmValue(String key, String name) throws FatalInstallerError; + + /** Sets a registry value inside a registry key in HKLM. */ void setHklmValue(String key, String name, String value) throws FatalInstallerError; + + /** Deletes a registry value inside a registry key in HKLM. */ void deleteHklmValue(String key, String name) throws FatalInstallerError; } diff --git a/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java b/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java index bc7ef4c96..9be442fb0 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/windows/WindowsRegistry.java @@ -10,6 +10,16 @@ */ public class WindowsRegistry implements IRegistry { + /** The key under which machine-global environment variables are stored. */ + public static final String ENVIRONMENT_REGISTRY_KEY = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; + + /** The singleton instance. */ + public static final WindowsRegistry INSTANCE = new WindowsRegistry(); + + private WindowsRegistry() { + // private constructor to force usage of singleton + } + @Override public String getHklmValue(String key, String name) throws FatalInstallerError { try { diff --git a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java index 0348c46c6..79b4947cc 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java @@ -174,7 +174,6 @@ void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { } } - @EnabledOnOs(OS.LINUX) @Test void uninstallChangingEtcEnvironmentFails() throws FatalInstallerError { install(); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java index 9a45b270b..1483d997d 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java @@ -5,6 +5,7 @@ import com.teamscale.profiler.installer.Installer; import com.teamscale.profiler.installer.TeamscaleCredentials; import com.teamscale.profiler.installer.windows.IRegistry; +import com.teamscale.profiler.installer.windows.WindowsRegistry; import okhttp3.HttpUrl; import org.junit.jupiter.api.Test; @@ -13,6 +14,10 @@ import static org.assertj.core.api.Assertions.assertThat; +/** + * Tests the installation step that sets Windows environment variables. This test mocks the registry so we can test this + * step on any OS. + */ class InstallWindowsSystemEnvironmentStepTest { private static final TeamscaleCredentials CREDENTIALS = new TeamscaleCredentials( @@ -41,11 +46,11 @@ public void deleteHklmValue(String key, String name) { } public String getVariable(String name) { - return getHklmValue(InstallWindowsSystemEnvironmentStep.ENVIRONMENT_REGISTRY_KEY, name); + return getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name); } public void setVariable(String name, String value) { - setHklmValue(InstallWindowsSystemEnvironmentStep.ENVIRONMENT_REGISTRY_KEY, name, value); + setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name, value); } } diff --git a/installer/src/test/java/com/teamscale/profiler/installer/windows/WindowsRegistryTest.java b/installer/src/test/java/com/teamscale/profiler/installer/windows/WindowsRegistryTest.java new file mode 100644 index 000000000..675cdfc93 --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/windows/WindowsRegistryTest.java @@ -0,0 +1,40 @@ +package com.teamscale.profiler.installer.windows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests our registry access class. + */ +@EnabledOnOs(OS.WINDOWS) +class WindowsRegistryTest { + + private static final String VARIABLE = "TEAMSCALE_JAVA_PROFILER_WINDOWS_REGISTRY_TEST"; + + @BeforeEach + void clearRegistry() throws Exception { + Runtime.getRuntime() + .exec(new String[]{"reg", "delete", "HKLM\\" + WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, "/v", VARIABLE}); + } + + @Test + void testAllFunctions() throws Exception { + assertThat(WindowsRegistry.INSTANCE.getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE)).isNull(); + + WindowsRegistry.INSTANCE.setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE, "foobar"); + assertThat(WindowsRegistry.INSTANCE.getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE)).isEqualTo( + "foobar"); + + WindowsRegistry.INSTANCE.setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE, "goo"); + assertThat(WindowsRegistry.INSTANCE.getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE)).isEqualTo( + "goo"); + + WindowsRegistry.INSTANCE.deleteHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE); + assertThat(WindowsRegistry.INSTANCE.getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, VARIABLE)).isNull(); + } + +} \ No newline at end of file From 43896e586a12bd5d7010f650f275e644b5630b0e Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 08:43:03 +0100 Subject: [PATCH 05/62] add Windows test and refactor existing tests --- .../profiler/installer/Installer.java | 10 +- .../installer/AllPlatformsInstallerTest.java | 159 ++++++++++++++++++ .../installer/LinuxInstallerTest.java | 119 +++---------- .../profiler/installer/MockRegistry.java | 35 ++++ .../installer/WindowsInstallerTest.java | 106 ++++++++++++ ...stallWindowsSystemEnvironmentStepTest.java | 42 +---- 6 files changed, 335 insertions(+), 136 deletions(-) create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java diff --git a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java index f60041dc3..34cf67f9c 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java @@ -5,6 +5,7 @@ import com.teamscale.profiler.installer.steps.InstallEtcEnvironmentStep; import com.teamscale.profiler.installer.steps.InstallSystemdStep; import com.teamscale.profiler.installer.steps.InstallWindowsSystemEnvironmentStep; +import com.teamscale.profiler.installer.windows.IRegistry; import com.teamscale.profiler.installer.windows.WindowsRegistry; import org.conqat.lib.commons.collections.CollectionUtils; import org.conqat.lib.commons.system.SystemUtils; @@ -65,11 +66,13 @@ private static Path getDefaultSourceDirectory() { * @param sourceDirectory directory that contains the profiler binaries and support files to install. * @param installDirectory directory to which to install the profiler. * @param etcDirectory on Linux: the /etc directory + * @param registry the Windows registry (not used on Linux) */ - public Installer(Path sourceDirectory, Path installDirectory, Path etcDirectory, boolean reloadSystemdDaemon) { + public Installer(Path sourceDirectory, Path installDirectory, Path etcDirectory, boolean reloadSystemdDaemon, + IRegistry registry) { EnvironmentMap environmentVariables = getEnvironmentVariables(installDirectory); this.steps = Arrays.asList(new InstallAgentFilesStep(sourceDirectory, installDirectory), - new InstallWindowsSystemEnvironmentStep(environmentVariables, WindowsRegistry.INSTANCE), + new InstallWindowsSystemEnvironmentStep(environmentVariables, registry), new InstallEtcEnvironmentStep(etcDirectory, environmentVariables), new InstallSystemdStep(etcDirectory, environmentVariables, reloadSystemdDaemon)); } @@ -107,7 +110,8 @@ public static int install(TeamscaleCredentials credentials) { } private static Installer getDefaultInstaller() { - return new Installer(getDefaultSourceDirectory(), DEFAULT_INSTALL_DIRECTORY, DEFAULT_ETC_DIRECTORY, true); + return new Installer(getDefaultSourceDirectory(), DEFAULT_INSTALL_DIRECTORY, DEFAULT_ETC_DIRECTORY, true, + WindowsRegistry.INSTANCE); } diff --git a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java new file mode 100644 index 000000000..6174e3916 --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java @@ -0,0 +1,159 @@ +package com.teamscale.profiler.installer; + +import okhttp3.HttpUrl; +import org.conqat.lib.commons.filesystem.FileSystemUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class AllPlatformsInstallerTest { + + private static final int TEAMSCALE_PORT = 8059; + private static final String FILE_TO_INSTALL_CONTENT = "install-me"; + private static final String NESTED_FILE_CONTENT = "nested-file"; + private static final String TEAMSCALE_URL = "http://localhost:" + TEAMSCALE_PORT + "/"; + + + private Path sourceDirectory; + private Path targetDirectory; + + private Path installedFile; + private Path installedNestedFile; + private Path installedTeamscaleProperties; + + private static MockTeamscale mockTeamscale; + + @BeforeEach + void setUpSourceDirectory() throws IOException { + sourceDirectory = Files.createTempDirectory("InstallerTest-source"); + targetDirectory = Files.createTempDirectory("InstallerTest-target").resolve("profiler"); + + Path fileToInstall = sourceDirectory.resolve("install-me.txt"); + Files.write(fileToInstall, FILE_TO_INSTALL_CONTENT.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + + Path nestedFileToInstall = sourceDirectory.resolve("lib/teamscale-jacoco-agent.jar"); + Files.createDirectories(nestedFileToInstall.getParent()); + Files.write(nestedFileToInstall, NESTED_FILE_CONTENT.getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE); + + installedFile = targetDirectory.resolve(sourceDirectory.relativize(fileToInstall)); + installedNestedFile = targetDirectory.resolve(sourceDirectory.relativize(nestedFileToInstall)); + installedTeamscaleProperties = targetDirectory.resolve("teamscale.properties"); + } + + @BeforeAll + static void startFakeTeamscale() { + mockTeamscale = new MockTeamscale(TEAMSCALE_PORT); + } + + @AfterAll + static void stopFakeTeamscale() { + mockTeamscale.shutdown(); + } + + @Test + void successfulInstallation() throws FatalInstallerError, IOException { + install(); + + assertThat(installedFile).exists().content().isEqualTo(FILE_TO_INSTALL_CONTENT); + assertThat(installedNestedFile).exists().content().isEqualTo(NESTED_FILE_CONTENT); + + assertThat(installedTeamscaleProperties).exists(); + Properties properties = FileSystemUtils.readProperties(installedTeamscaleProperties.toFile()); + assertThat(properties.keySet()).containsExactlyInAnyOrder("url", "username", "accesskey"); + assertThat(properties.getProperty("url")).isEqualTo(TEAMSCALE_URL); + assertThat(properties.getProperty("username")).isEqualTo("user"); + assertThat(properties.getProperty("accesskey")).isEqualTo("accesskey"); + } + + @Test + void distributionChangedByUser() throws IOException { + Files.delete(sourceDirectory.resolve("lib/teamscale-jacoco-agent.jar")); + assertThatThrownBy(this::install) + .hasMessageContaining("It looks like you moved the installer"); + } + + @Test + void successfulUninstallation() throws FatalInstallerError { + install(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); + + assertThat(errorReporter.wereErrorsReported()).isFalse(); + assertThat(targetDirectory).doesNotExist(); + } + + @Test + void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { + install(); + assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); + + Installer.UninstallerErrorReporter errorReporter = uninstall(); + + assertThat(errorReporter.wereErrorsReported()).isTrue(); + + assertThat(targetDirectory).exists(); + assertThat(installedTeamscaleProperties).exists(); + // nested files must be removed if possible + assertThat(installedNestedFile).doesNotExist(); + } + + @Test + void nonexistantTeamscaleUrl() { + assertThatThrownBy(() -> install("http://does-not-exist:8080")) + .hasMessageContaining("could not be resolved"); + assertThat(targetDirectory).doesNotExist(); + } + + @Test + void connectionRefused() { + assertThatThrownBy(() -> install("http://localhost:" + (TEAMSCALE_PORT + 1))) + .hasMessageContaining("refused a connection"); + assertThat(targetDirectory).doesNotExist(); + } + + @Test + void httpsInsteadOfHttp() { + assertThatThrownBy(() -> install("https://localhost:" + TEAMSCALE_PORT)) + .hasMessageContaining("configured for HTTPS, not HTTP"); + assertThat(targetDirectory).doesNotExist(); + } + + @Test + void profilerAlreadyInstalled() throws IOException { + Files.createDirectories(targetDirectory); + assertThatThrownBy(this::install).hasMessageContaining("Path already exists"); + } + + @Test + void installDirectoryNotWritable() { + assertThat(targetDirectory.getParent().toFile().setReadOnly()).isTrue(); + assertThatThrownBy(() -> install(TEAMSCALE_URL)).hasMessageContaining("Cannot create directory"); + } + + private void install() throws FatalInstallerError { + install(TEAMSCALE_URL); + } + + private void install(String teamscaleUrl) throws FatalInstallerError { + new Installer(sourceDirectory, targetDirectory, Paths.get("/etc"), false, new MockRegistry()).runInstall( + new TeamscaleCredentials(HttpUrl.get(teamscaleUrl), "user", "accesskey")); + } + + private Installer.UninstallerErrorReporter uninstall() { + return new Installer(sourceDirectory, targetDirectory, + Paths.get("/etc"), false, new MockRegistry()).runUninstall(); + } + +} diff --git a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java index 79b4947cc..7fd749679 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java @@ -1,8 +1,7 @@ package com.teamscale.profiler.installer; +import com.teamscale.profiler.installer.windows.WindowsRegistry; import okhttp3.HttpUrl; -import org.conqat.lib.commons.filesystem.FileSystemUtils; -import org.conqat.lib.commons.system.SystemUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -15,11 +14,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.Properties; import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; @EnabledOnOs(OS.LINUX) class LinuxInstallerTest { @@ -36,7 +33,6 @@ class LinuxInstallerTest { private Path etcDirectory; private Path installedFile; - private Path installedNestedFile; private Path installedTeamscaleProperties; private Path installedAgentLibrary; @@ -68,7 +64,6 @@ void setUpSourceDirectory() throws IOException { StandardOpenOption.CREATE); installedFile = targetDirectory.resolve(sourceDirectory.relativize(fileToInstall)); - installedNestedFile = targetDirectory.resolve(sourceDirectory.relativize(nestedFileToInstall)); installedTeamscaleProperties = targetDirectory.resolve("teamscale.properties"); installedAgentLibrary = targetDirectory.resolve("lib/teamscale-jacoco-agent.jar"); } @@ -87,58 +82,33 @@ static void stopFakeTeamscale() { void successfulInstallation() throws FatalInstallerError, IOException { install(); - assertThat(installedFile).exists().content().isEqualTo(FILE_TO_INSTALL_CONTENT); - assertThat(installedNestedFile).exists().content().isEqualTo(NESTED_FILE_CONTENT); + assertThat(Files.getPosixFilePermissions(installedTeamscaleProperties)).contains(OTHERS_READ); + assertThat(Files.getPosixFilePermissions(installedFile)).contains(OTHERS_READ); - assertThat(installedTeamscaleProperties).exists(); - Properties properties = FileSystemUtils.readProperties(installedTeamscaleProperties.toFile()); - assertThat(properties.keySet()).containsExactlyInAnyOrder("url", "username", "accesskey"); - assertThat(properties.getProperty("url")).isEqualTo(TEAMSCALE_URL); - assertThat(properties.getProperty("username")).isEqualTo("user"); - assertThat(properties.getProperty("accesskey")).isEqualTo("accesskey"); - - if (SystemUtils.isLinux()) { - assertThat(Files.getPosixFilePermissions(installedTeamscaleProperties)).contains(OTHERS_READ); - assertThat(Files.getPosixFilePermissions(installedFile)).contains(OTHERS_READ); - - assertThat(environmentFile).content().isEqualTo(ENVIRONMENT_CONTENT - + "\nJAVA_TOOL_OPTIONS=-javaagent:" + installedAgentLibrary - + "\n_JAVA_OPTIONS=-javaagent:" + installedAgentLibrary + "\n"); - - assertThat(systemdConfig).content().isEqualTo("[Manager]" - + "\nDefaultEnvironment=JAVA_TOOL_OPTIONS=-javaagent:" + installedAgentLibrary - + " _JAVA_OPTIONS=-javaagent:" + installedAgentLibrary + "\n"); - } - } + assertThat(environmentFile).content().isEqualTo(ENVIRONMENT_CONTENT + + "\nJAVA_TOOL_OPTIONS=-javaagent:" + installedAgentLibrary + + "\n_JAVA_OPTIONS=-javaagent:" + installedAgentLibrary + "\n"); - @Test - void distributionChangedByUser() throws IOException { - Files.delete(sourceDirectory.resolve("lib/teamscale-jacoco-agent.jar")); - assertThatThrownBy(this::install) - .hasMessageContaining("It looks like you moved the installer"); + assertThat(systemdConfig).content().isEqualTo("[Manager]" + + "\nDefaultEnvironment=JAVA_TOOL_OPTIONS=-javaagent:" + installedAgentLibrary + + " _JAVA_OPTIONS=-javaagent:" + installedAgentLibrary + "\n"); } @Test void successfulUninstallation() throws FatalInstallerError { install(); - Installer.UninstallerErrorReporter errorReporter = new Installer(sourceDirectory, targetDirectory, - etcDirectory, false).runUninstall(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter.wereErrorsReported()).isFalse(); - - assertThat(targetDirectory).doesNotExist(); - if (SystemUtils.isLinux()) { - assertThat(environmentFile).exists().content().isEqualTo(ENVIRONMENT_CONTENT); - assertThat(systemdConfig).doesNotExist(); - } + assertThat(environmentFile).exists().content().isEqualTo(ENVIRONMENT_CONTENT); + assertThat(systemdConfig).doesNotExist(); } @Test void uninstallSuccessfullyEvenIfSystemDConfigWasManuallyRemoved() throws FatalInstallerError, IOException { install(); Files.delete(systemdConfig); - Installer.UninstallerErrorReporter errorReporter = new Installer(sourceDirectory, targetDirectory, - etcDirectory, false).runUninstall(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter.wereErrorsReported()).isFalse(); } @@ -147,8 +117,7 @@ void uninstallSuccessfullyEvenIfSystemDConfigWasManuallyRemoved() throws FatalIn void uninstallSuccessfullyEvenIfEnvironmentFileDoesntExist() throws FatalInstallerError, IOException { install(); Files.delete(environmentFile); - Installer.UninstallerErrorReporter errorReporter = new Installer(sourceDirectory, targetDirectory, - etcDirectory, false).runUninstall(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter.wereErrorsReported()).isFalse(); } @@ -158,20 +127,11 @@ void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { install(); assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); - Installer.UninstallerErrorReporter errorReporter = new Installer(sourceDirectory, targetDirectory, - etcDirectory, false).runUninstall(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter.wereErrorsReported()).isTrue(); - - assertThat(targetDirectory).exists(); - assertThat(installedTeamscaleProperties).exists(); - // nested files must be removed if possible - assertThat(installedNestedFile).doesNotExist(); - - if (SystemUtils.isLinux()) { - assertThat(environmentFile).exists().content().isEqualTo(ENVIRONMENT_CONTENT); - assertThat(systemdConfig).doesNotExist(); - } + assertThat(environmentFile).exists().content().isEqualTo(ENVIRONMENT_CONTENT); + assertThat(systemdConfig).doesNotExist(); } @Test @@ -179,8 +139,7 @@ void uninstallChangingEtcEnvironmentFails() throws FatalInstallerError { install(); assertThat(environmentFile.toFile().setWritable(false, false)).isTrue(); - Installer.UninstallerErrorReporter errorReporter = new Installer(sourceDirectory, targetDirectory, - etcDirectory, false).runUninstall(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter.wereErrorsReported()).isTrue(); @@ -207,46 +166,14 @@ void noSystemd() throws FatalInstallerError, IOException { assertThat(systemdConfig).doesNotExist(); } - @Test - void nonexistantTeamscaleUrl() { - assertThatThrownBy(() -> install("http://does-not-exist:8080")) - .hasMessageContaining("could not be resolved"); - assertThat(targetDirectory).doesNotExist(); - } - - @Test - void connectionRefused() { - assertThatThrownBy(() -> install("http://localhost:" + (TEAMSCALE_PORT + 1))) - .hasMessageContaining("refused a connection"); - assertThat(targetDirectory).doesNotExist(); - } - - @Test - void httpsInsteadOfHttp() { - assertThatThrownBy(() -> install("https://localhost:" + TEAMSCALE_PORT)) - .hasMessageContaining("configured for HTTPS, not HTTP"); - assertThat(targetDirectory).doesNotExist(); - } - - @Test - void profilerAlreadyInstalled() throws IOException { - Files.createDirectories(targetDirectory); - assertThatThrownBy(this::install).hasMessageContaining("Path already exists"); - } - - @Test - void installDirectoryNotWritable() { - assertThat(targetDirectory.getParent().toFile().setReadOnly()).isTrue(); - assertThatThrownBy(() -> install(TEAMSCALE_URL)).hasMessageContaining("Cannot create directory"); - } - private void install() throws FatalInstallerError { - install(TEAMSCALE_URL); + new Installer(sourceDirectory, targetDirectory, etcDirectory, false, WindowsRegistry.INSTANCE).runInstall( + new TeamscaleCredentials(HttpUrl.get(LinuxInstallerTest.TEAMSCALE_URL), "user", "accesskey")); } - private void install(String teamscaleUrl) throws FatalInstallerError { - new Installer(sourceDirectory, targetDirectory, etcDirectory, false).runInstall( - new TeamscaleCredentials(HttpUrl.get(teamscaleUrl), "user", "accesskey")); + private Installer.UninstallerErrorReporter uninstall() { + return new Installer(sourceDirectory, targetDirectory, + etcDirectory, false, WindowsRegistry.INSTANCE).runUninstall(); } } diff --git a/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java b/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java new file mode 100644 index 000000000..300df44d3 --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java @@ -0,0 +1,35 @@ +package com.teamscale.profiler.installer; + +import com.teamscale.profiler.installer.windows.IRegistry; +import com.teamscale.profiler.installer.windows.WindowsRegistry; + +import java.util.HashMap; +import java.util.Map; + +public class MockRegistry implements IRegistry { + + private final Map values = new HashMap<>(); + + @Override + public String getHklmValue(String key, String name) { + return values.get(key + "\\" + name); + } + + @Override + public void setHklmValue(String key, String name, String value) { + values.put(key + "\\" + name, value); + } + + @Override + public void deleteHklmValue(String key, String name) { + values.remove(key + "\\" + name); + } + + public String getVariable(String name) { + return getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name); + } + + public void setVariable(String name, String value) { + setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name, value); + } +} diff --git a/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java new file mode 100644 index 000000000..1e72bcde3 --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java @@ -0,0 +1,106 @@ +package com.teamscale.profiler.installer; + +import okhttp3.HttpUrl; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +import static org.assertj.core.api.Assertions.assertThat; + +@EnabledOnOs(OS.WINDOWS) +class WindowsInstallerTest { + + private static final int TEAMSCALE_PORT = 8059; + private static final String FILE_TO_INSTALL_CONTENT = "install-me"; + private static final String NESTED_FILE_CONTENT = "nested-file"; + private static final String TEAMSCALE_URL = "http://localhost:" + TEAMSCALE_PORT + "/"; + + + private Path sourceDirectory; + private Path targetDirectory; + + private Path installedAgentLibrary; + + private static MockTeamscale mockTeamscale; + + private MockRegistry registry; + + @BeforeEach + void setUpSourceDirectory() throws IOException { + sourceDirectory = Files.createTempDirectory("InstallerTest-source"); + targetDirectory = Files.createTempDirectory("InstallerTest-target").resolve("profiler"); + + Path fileToInstall = sourceDirectory.resolve("install-me.txt"); + Files.write(fileToInstall, FILE_TO_INSTALL_CONTENT.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + + Path nestedFileToInstall = sourceDirectory.resolve("lib/teamscale-jacoco-agent.jar"); + Files.createDirectories(nestedFileToInstall.getParent()); + Files.write(nestedFileToInstall, NESTED_FILE_CONTENT.getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE); + + installedAgentLibrary = targetDirectory.resolve("lib/teamscale-jacoco-agent.jar"); + + registry = new MockRegistry(); + } + + @BeforeAll + static void startFakeTeamscale() { + mockTeamscale = new MockTeamscale(TEAMSCALE_PORT); + } + + @AfterAll + static void stopFakeTeamscale() { + mockTeamscale.shutdown(); + } + + @Test + void successfulInstallation() throws FatalInstallerError { + install(); + + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo("-javaagent:" + installedAgentLibrary); + assertThat(registry.getVariable("JAVA_TOOL_OPTIONS")).isEqualTo("-javaagent:" + installedAgentLibrary); + } + + @Test + void successfulUninstallation() throws FatalInstallerError { + install(); + Installer.UninstallerErrorReporter errorReporter = uninstall(); + + assertThat(errorReporter.wereErrorsReported()).isFalse(); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isNull(); + assertThat(registry.getVariable("JAVA_TOOL_OPTIONS")).isNull(); + } + + @Test + void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { + install(); + assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); + + Installer.UninstallerErrorReporter errorReporter = uninstall(); + + assertThat(errorReporter.wereErrorsReported()).isTrue(); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isNull(); + assertThat(registry.getVariable("JAVA_TOOL_OPTIONS")).isNull(); + } + + private void install() throws FatalInstallerError { + new Installer(sourceDirectory, targetDirectory, Paths.get("/etc"), false, registry).runInstall( + new TeamscaleCredentials(HttpUrl.get(WindowsInstallerTest.TEAMSCALE_URL), "user", "accesskey")); + } + + private Installer.UninstallerErrorReporter uninstall() { + return new Installer(sourceDirectory, targetDirectory, + Paths.get("/etc"), false, registry).runUninstall(); + } + +} diff --git a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java index 1483d997d..7cc665dd3 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java @@ -3,15 +3,11 @@ import com.teamscale.profiler.installer.EnvironmentMap; import com.teamscale.profiler.installer.FatalInstallerError; import com.teamscale.profiler.installer.Installer; +import com.teamscale.profiler.installer.MockRegistry; import com.teamscale.profiler.installer.TeamscaleCredentials; -import com.teamscale.profiler.installer.windows.IRegistry; -import com.teamscale.profiler.installer.windows.WindowsRegistry; import okhttp3.HttpUrl; import org.junit.jupiter.api.Test; -import java.util.HashMap; -import java.util.Map; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -26,37 +22,9 @@ class InstallWindowsSystemEnvironmentStepTest { private static final EnvironmentMap environment = new EnvironmentMap("_JAVA_OPTIONS", "\"-javaagent:C:\\Program Files\\foo.jar\""); - private static class FakeRegistry implements IRegistry { - - private final Map values = new HashMap<>(); - - @Override - public String getHklmValue(String key, String name) { - return values.get(key + "\\" + name); - } - - @Override - public void setHklmValue(String key, String name, String value) { - values.put(key + "\\" + name, value); - } - - @Override - public void deleteHklmValue(String key, String name) { - values.remove(key + "\\" + name); - } - - public String getVariable(String name) { - return getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name); - } - - public void setVariable(String name, String value) { - setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name, value); - } - } - @Test void successfulInstall() throws FatalInstallerError { - FakeRegistry registry = new FakeRegistry(); + MockRegistry registry = new MockRegistry(); new InstallWindowsSystemEnvironmentStep(environment, registry).install(CREDENTIALS); assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo(environment.getMap().get("_JAVA_OPTIONS")); @@ -64,7 +32,7 @@ void successfulInstall() throws FatalInstallerError { @Test void successfulUninstall() throws FatalInstallerError { - FakeRegistry registry = new FakeRegistry(); + MockRegistry registry = new MockRegistry(); Installer.UninstallerErrorReporter errorReporter = new Installer.UninstallerErrorReporter(); new InstallWindowsSystemEnvironmentStep(environment, registry).install(CREDENTIALS); @@ -76,7 +44,7 @@ void successfulUninstall() throws FatalInstallerError { @Test void addAndRemoveProfiler() throws FatalInstallerError { - FakeRegistry registry = new FakeRegistry(); + MockRegistry registry = new MockRegistry(); InstallWindowsSystemEnvironmentStep.addProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo("-javaagent:foo.jar"); @@ -87,7 +55,7 @@ void addAndRemoveProfiler() throws FatalInstallerError { @Test void addAndRemoveProfilerWithPreviousValue() throws FatalInstallerError { - FakeRegistry registry = new FakeRegistry(); + MockRegistry registry = new MockRegistry(); registry.setVariable("_JAVA_OPTIONS", "-javaagent:other.jar"); InstallWindowsSystemEnvironmentStep.addProfiler("_JAVA_OPTIONS", "-javaagent:foo.jar", registry); From fe25c087b5ddcaf13499fa095ad4ae3941aebaea Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 08:46:21 +0100 Subject: [PATCH 06/62] add windows build --- .github/workflows/actions.yml | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index a6a1cf281..4e33575cc 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -8,8 +8,8 @@ on: branches: '**' jobs: - build: - name: Build & Deploy + build-linux: + name: Build Linux & Deploy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -74,6 +74,33 @@ jobs: message: 'Coverage' files: '**/jacocoTestReport.xml' + build-windows: + name: Build Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Set up GraalVM + uses: graalvm/setup-graalvm@v1 + with: + java-version: '17.0.8' + distribution: 'graalvm-community' + github-token: ${{ secrets.GITHUB_TOKEN }} + set-java-home: true + - name: Build with Gradle + run: ./gradlew build + - name: Upload coverage to Teamscale + if: always() && github.event_name == 'push' + uses: cqse/teamscale-upload-action@v2.8.2 + with: + server: 'https://cqse.teamscale.io' + project: 'teamscale-jacoco-agent' + user: ${{ secrets.CQSE_TEAMSCALE_IO_USER }} + accesskey: ${{ secrets.CQSE_TEAMSCALE_IO_ACCESSKEY }} + partition: 'Coverage Windows' + format: 'JACOCO' + message: 'Coverage Windows' + files: '**/jacocoTestReport.xml' + docker: runs-on: ubuntu-latest steps: From 6ec4999b91fb85fd716d816ec1a43bea8a15e211 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 08:54:26 +0100 Subject: [PATCH 07/62] fix TS findings --- .../com/teamscale/profiler/installer/Installer.java | 4 ++-- .../com/teamscale/profiler/installer/steps/IStep.java | 5 +++-- .../installer/steps/InstallEtcEnvironmentStep.java | 4 ++-- .../profiler/installer/steps/InstallSystemdStep.java | 4 ++-- .../steps/InstallWindowsSystemEnvironmentStep.java | 4 ++-- .../com/teamscale/profiler/installer/MockRegistry.java | 5 +++++ .../steps/InstallWindowsSystemEnvironmentStepTest.java | 10 +++++----- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java index 34cf67f9c..644dee42c 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java @@ -143,7 +143,7 @@ public static int uninstall() { public void runInstall(TeamscaleCredentials credentials) throws FatalInstallerError { TeamscaleUtils.checkTeamscaleConnection(credentials); for (IStep step : steps) { - if (!step.shouldRun()) { + if (step.shouldNotRun()) { continue; } step.install(credentials); @@ -157,7 +157,7 @@ public void runInstall(TeamscaleCredentials credentials) throws FatalInstallerEr public UninstallerErrorReporter runUninstall() { UninstallerErrorReporter errorReporter = new UninstallerErrorReporter(); for (IStep step : CollectionUtils.reverse(steps)) { - if (!step.shouldRun()) { + if (step.shouldNotRun()) { continue; } diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java index 6050e6c93..8cf8761a3 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/IStep.java @@ -20,8 +20,9 @@ public interface IStep { */ void uninstall(IUninstallErrorReporter errorReporter); - default boolean shouldRun() { - return true; + /** Determines whether this step should not be run, e.g. because this step is not applicable to the current OS. */ + default boolean shouldNotRun() { + return false; } /** diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java index e5447c446..499f6f0a4 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallEtcEnvironmentStep.java @@ -29,8 +29,8 @@ public InstallEtcEnvironmentStep(Path etcDirectory, EnvironmentMap environmentMa } @Override - public boolean shouldRun() { - return SystemUtils.isLinux(); + public boolean shouldNotRun() { + return !SystemUtils.isLinux(); } @Override diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java index 08752b625..97e4b2eef 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallSystemdStep.java @@ -30,8 +30,8 @@ public InstallSystemdStep(Path etcDirectory, EnvironmentMap environmentMap, bool } @Override - public boolean shouldRun() { - return SystemUtils.isLinux(); + public boolean shouldNotRun() { + return !SystemUtils.isLinux(); } @Override diff --git a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java index f4d50e936..3f2a23fac 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStep.java @@ -22,8 +22,8 @@ public InstallWindowsSystemEnvironmentStep(EnvironmentMap environmentMap, IRegis } @Override - public boolean shouldRun() { - return SystemUtils.isWindows(); + public boolean shouldNotRun() { + return !SystemUtils.isWindows(); } @Override diff --git a/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java b/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java index 300df44d3..a95228009 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java @@ -6,6 +6,9 @@ import java.util.HashMap; import java.util.Map; +/** + * Mock of {@link IRegistry} to allow tests without actually changing the Windows registry. + */ public class MockRegistry implements IRegistry { private final Map values = new HashMap<>(); @@ -25,10 +28,12 @@ public void deleteHklmValue(String key, String name) { values.remove(key + "\\" + name); } + /** Reads the given environment variable from the registry. */ public String getVariable(String name) { return getHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name); } + /** Sets the given environment variable in the registry. */ public void setVariable(String name, String value) { setHklmValue(WindowsRegistry.ENVIRONMENT_REGISTRY_KEY, name, value); } diff --git a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java index 7cc665dd3..bcd0b4ed5 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java @@ -19,15 +19,15 @@ class InstallWindowsSystemEnvironmentStepTest { private static final TeamscaleCredentials CREDENTIALS = new TeamscaleCredentials( HttpUrl.get("http://localhost:" + 8058 + "/"), "user", "accesskey"); - private static final EnvironmentMap environment = new EnvironmentMap("_JAVA_OPTIONS", + private static final EnvironmentMap ENVIRONMENT = new EnvironmentMap("_JAVA_OPTIONS", "\"-javaagent:C:\\Program Files\\foo.jar\""); @Test void successfulInstall() throws FatalInstallerError { MockRegistry registry = new MockRegistry(); - new InstallWindowsSystemEnvironmentStep(environment, registry).install(CREDENTIALS); + new InstallWindowsSystemEnvironmentStep(ENVIRONMENT, registry).install(CREDENTIALS); - assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo(environment.getMap().get("_JAVA_OPTIONS")); + assertThat(registry.getVariable("_JAVA_OPTIONS")).isEqualTo(ENVIRONMENT.getMap().get("_JAVA_OPTIONS")); } @Test @@ -35,8 +35,8 @@ void successfulUninstall() throws FatalInstallerError { MockRegistry registry = new MockRegistry(); Installer.UninstallerErrorReporter errorReporter = new Installer.UninstallerErrorReporter(); - new InstallWindowsSystemEnvironmentStep(environment, registry).install(CREDENTIALS); - new InstallWindowsSystemEnvironmentStep(environment, registry).uninstall(errorReporter); + new InstallWindowsSystemEnvironmentStep(ENVIRONMENT, registry).install(CREDENTIALS); + new InstallWindowsSystemEnvironmentStep(ENVIRONMENT, registry).uninstall(errorReporter); assertThat(errorReporter.wereErrorsReported()).isFalse(); assertThat(registry.getVariable("_JAVA_OPTIONS")).isNull(); From ed160d4a9078c3bfa9f728740607a99a031f56bd Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:02:14 +0100 Subject: [PATCH 08/62] fix linux tests --- .../teamscale/profiler/installer/Installer.java | 15 ++++++++------- .../installer/AllPlatformsInstallerTest.java | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java index 644dee42c..06926e881 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java @@ -17,27 +17,28 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; /** Installs the agent system-globally. */ public class Installer { private static final Path DEFAULT_INSTALL_DIRECTORY = windowsOrLinux( - Paths.get(System.getenv("ProgramFiles")), - Paths.get("/opt/teamscale-profiler/java") + () -> Paths.get(System.getenv("ProgramFiles")), + () -> Paths.get("/opt/teamscale-profiler/java") ); private static final Path DEFAULT_ETC_DIRECTORY = Paths.get("/etc"); private static final String RERUN_ADVICE = windowsOrLinux( - "Try running this installer as Administrator.", - "Try running this installer as root, e.g. with sudo." + () -> "Try running this installer as Administrator.", + () -> "Try running this installer as root, e.g. with sudo." ); - private static T windowsOrLinux(T windowsValue, T linuxValue) { + private static T windowsOrLinux(Supplier windowsSupplier, Supplier linuxSupplier) { if (SystemUtils.isWindows()) { - return windowsValue; + return windowsSupplier.get(); } else { - return linuxValue; + return linuxSupplier.get(); } } diff --git a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java index 6174e3916..ee9c44e10 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java @@ -11,7 +11,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Properties; @@ -28,6 +27,7 @@ class AllPlatformsInstallerTest { private Path sourceDirectory; private Path targetDirectory; + private Path etcDirectory; private Path installedFile; private Path installedNestedFile; @@ -39,6 +39,7 @@ class AllPlatformsInstallerTest { void setUpSourceDirectory() throws IOException { sourceDirectory = Files.createTempDirectory("InstallerTest-source"); targetDirectory = Files.createTempDirectory("InstallerTest-target").resolve("profiler"); + etcDirectory = Files.createTempDirectory("InstallerTest-etc"); Path fileToInstall = sourceDirectory.resolve("install-me.txt"); Files.write(fileToInstall, FILE_TO_INSTALL_CONTENT.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); @@ -147,13 +148,12 @@ private void install() throws FatalInstallerError { } private void install(String teamscaleUrl) throws FatalInstallerError { - new Installer(sourceDirectory, targetDirectory, Paths.get("/etc"), false, new MockRegistry()).runInstall( + new Installer(sourceDirectory, targetDirectory, etcDirectory, false, new MockRegistry()).runInstall( new TeamscaleCredentials(HttpUrl.get(teamscaleUrl), "user", "accesskey")); } private Installer.UninstallerErrorReporter uninstall() { - return new Installer(sourceDirectory, targetDirectory, - Paths.get("/etc"), false, new MockRegistry()).runUninstall(); + return new Installer(sourceDirectory, targetDirectory, etcDirectory, false, new MockRegistry()).runUninstall(); } } From 7188cbfd1f038da1de4102f6ad972dd083bf43e8 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:06:49 +0100 Subject: [PATCH 09/62] advise Windows users to restart after installation --- .../teamscale/profiler/installer/Installer.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java index 06926e881..5d364f3a6 100644 --- a/installer/src/main/java/com/teamscale/profiler/installer/Installer.java +++ b/installer/src/main/java/com/teamscale/profiler/installer/Installer.java @@ -34,6 +34,11 @@ public class Installer { () -> "Try running this installer as root, e.g. with sudo." ); + private static final String RESTART_ADVICE = windowsOrLinux( + () -> "Please restart Windows to apply all changes.", + () -> "In an interactive session, you have to log out and log back in for the changes to take effect." + ); + private static T windowsOrLinux(Supplier windowsSupplier, Supplier linuxSupplier) { if (SystemUtils.isWindows()) { return windowsSupplier.get(); @@ -86,22 +91,19 @@ public Installer(Path sourceDirectory, Path installDirectory, Path etcDirectory, public static int install(TeamscaleCredentials credentials) { try { getDefaultInstaller().runInstall(credentials); - System.out.println("Installation successful. Profiler installed to " + DEFAULT_INSTALL_DIRECTORY); - if (SystemUtils.isLinux()) { - System.out.println("To use the profiler in your current session, please log out and back in."); - } - System.out.println("To activate the profiler for an application, set the environment variable:" + System.out.println("Installation successful. Profiler installed to " + DEFAULT_INSTALL_DIRECTORY + + "\n\nTo activate the profiler for an application, set the environment variable:" + "\nTEAMSCALE_JAVA_PROFILER_CONFIG_ID" + "\nIts value must be a valid profiler configuration ID defined in the Teamscale instance." + "\nThen, restart your application (for web applications: restart the app server)." - + "\nIn an interactive session, you have to log out and log back in for the changes to take effect."); + + "\n\n" + RESTART_ADVICE); return CommandLine.ExitCode.OK; } catch (PermissionError e) { e.printToStderr(); System.err.println( "\n\nInstallation failed because the installer had insufficient permissions to make the necessary" - + " changes on your system.\nSee above for error messages.\n" + RERUN_ADVICE); + + " changes on your system.\nSee above for error messages.\n\n" + RERUN_ADVICE); return RootCommand.EXIT_CODE_PERMISSION_ERROR; } catch (FatalInstallerError e) { e.printToStderr(); From 02ee449b07f0d741647c1cfed6a20ece4de6c54a Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:08:50 +0100 Subject: [PATCH 10/62] rename windows build step to "Test" since it is only there to run the tests on Windows --- .github/workflows/actions.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 4e33575cc..2931f75e8 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -74,8 +74,8 @@ jobs: message: 'Coverage' files: '**/jacocoTestReport.xml' - build-windows: - name: Build Windows + test-windows: + name: Test on Windows runs-on: windows-latest steps: - uses: actions/checkout@v4 From 3f329e9800bd197249c92c3c9230d2ed476a5453 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:27:25 +0100 Subject: [PATCH 11/62] fix warning and improve error message --- .../com/teamscale/jacoco/agent/util/TestUtils.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/agent/src/test/java/com/teamscale/jacoco/agent/util/TestUtils.java b/agent/src/test/java/com/teamscale/jacoco/agent/util/TestUtils.java index cde211b33..7c360b2c8 100644 --- a/agent/src/test/java/com/teamscale/jacoco/agent/util/TestUtils.java +++ b/agent/src/test/java/com/teamscale/jacoco/agent/util/TestUtils.java @@ -3,6 +3,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; /** * Test Utilities @@ -11,12 +14,15 @@ public class TestUtils { /** * Deletes all contents inside the coverage folder inside the agent directory */ - @SuppressWarnings("ResultOfMethodCallIgnored") public static void cleanAgentCoverageDirectory() throws IOException { Path coverageDir = AgentUtils.getAgentDirectory().resolve("coverage"); if (Files.exists(coverageDir)) { - Files.list(coverageDir).forEach(path -> path.toFile().delete()); + try (Stream stream = Files.list(coverageDir)) { + stream.forEach(path -> + assertThat(path.toFile().delete()).withFailMessage("Failed to delete " + path).isTrue()); + } Files.delete(coverageDir); } } + } From 20850163768c22ae8920f1a324c6b5e96c481e16 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:28:39 +0100 Subject: [PATCH 12/62] disable problematic test on Windows --- .../options/TeamscalePropertiesUtilsTest.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/agent/src/test/java/com/teamscale/jacoco/agent/options/TeamscalePropertiesUtilsTest.java b/agent/src/test/java/com/teamscale/jacoco/agent/options/TeamscalePropertiesUtilsTest.java index e0cf64dd7..3fa8ac774 100644 --- a/agent/src/test/java/com/teamscale/jacoco/agent/options/TeamscalePropertiesUtilsTest.java +++ b/agent/src/test/java/com/teamscale/jacoco/agent/options/TeamscalePropertiesUtilsTest.java @@ -3,6 +3,8 @@ import okhttp3.HttpUrl; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; import java.io.IOException; @@ -45,36 +47,44 @@ void successfulParsing() throws AgentOptionParseException, IOException { void missingUsername() throws IOException { Files.write(teamscalePropertiesPath, "url=http://test\naccesskey=key".getBytes(StandardCharsets.UTF_8)); assertThatThrownBy( - () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining("missing the username"); + () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining( + "missing the username"); } @Test void missingAccessKey() throws IOException { Files.write(teamscalePropertiesPath, "url=http://test\nusername=user".getBytes(StandardCharsets.UTF_8)); assertThatThrownBy( - () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining("missing the accesskey"); + () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining( + "missing the accesskey"); } @Test void missingUrl() throws IOException { Files.write(teamscalePropertiesPath, "username=user\nusername=user".getBytes(StandardCharsets.UTF_8)); assertThatThrownBy( - () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining("missing the url"); + () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining( + "missing the url"); } @Test void malformedUrl() throws IOException { Files.write(teamscalePropertiesPath, "url=$$**\nusername=user\nusername=user".getBytes(StandardCharsets.UTF_8)); assertThatThrownBy( - () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining("malformed URL"); + () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining( + "malformed URL"); } + /** This test doesn't work on Windows since {@link java.io.File#setReadable(boolean)} does not work there. */ + @DisabledOnOs(OS.LINUX) @Test void fileNotReadable() throws IOException { - Files.write(teamscalePropertiesPath, "url=http://test\nusername=user\nusername=user".getBytes(StandardCharsets.UTF_8)); + Files.write(teamscalePropertiesPath, + "url=http://test\nusername=user\nusername=user".getBytes(StandardCharsets.UTF_8)); assertThat(teamscalePropertiesPath.toFile().setReadable(false)).isTrue(); assertThatThrownBy( - () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining("Failed to read"); + () -> TeamscalePropertiesUtils.parseCredentials(teamscalePropertiesPath)).hasMessageContaining( + "Failed to read"); } } \ No newline at end of file From db232f0f1c4e743139544882880912cb678f00d1 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:38:31 +0100 Subject: [PATCH 13/62] provide nice asserts for error reporter --- .../installer/AllPlatformsInstallerTest.java | 11 ++++--- .../installer/LinuxInstallerTest.java | 15 +++++---- .../installer/WindowsInstallerTest.java | 7 +++-- ...stallWindowsSystemEnvironmentStepTest.java | 2 +- .../installer/{ => utils}/MockRegistry.java | 2 +- .../installer/{ => utils}/MockTeamscale.java | 2 +- .../utils/UninstallErrorReporterAssert.java | 31 +++++++++++++++++++ 7 files changed, 53 insertions(+), 17 deletions(-) rename installer/src/test/java/com/teamscale/profiler/installer/{ => utils}/MockRegistry.java (95%) rename installer/src/test/java/com/teamscale/profiler/installer/{ => utils}/MockTeamscale.java (95%) create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/utils/UninstallErrorReporterAssert.java diff --git a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java index ee9c44e10..ea86e64f9 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java @@ -1,5 +1,7 @@ package com.teamscale.profiler.installer; +import com.teamscale.profiler.installer.utils.MockRegistry; +import com.teamscale.profiler.installer.utils.MockTeamscale; import okhttp3.HttpUrl; import org.conqat.lib.commons.filesystem.FileSystemUtils; import org.junit.jupiter.api.AfterAll; @@ -14,6 +16,7 @@ import java.nio.file.StandardOpenOption; import java.util.Properties; +import static com.teamscale.profiler.installer.utils.UninstallErrorReporterAssert.assertThat; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -90,19 +93,19 @@ void distributionChangedByUser() throws IOException { void successfulUninstallation() throws FatalInstallerError { install(); Installer.UninstallerErrorReporter errorReporter = uninstall(); + assertThat(errorReporter).hadNoErrors(); - assertThat(errorReporter.wereErrorsReported()).isFalse(); assertThat(targetDirectory).doesNotExist(); } @Test void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { install(); - assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); + assertThat(targetDirectory.toFile().setWritable(false, false)) + .withFailMessage("Could not make target directory unwritable").isTrue(); Installer.UninstallerErrorReporter errorReporter = uninstall(); - - assertThat(errorReporter.wereErrorsReported()).isTrue(); + assertThat(errorReporter).hadErrors(); assertThat(targetDirectory).exists(); assertThat(installedTeamscaleProperties).exists(); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java index 7fd749679..e453645eb 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java @@ -1,5 +1,6 @@ package com.teamscale.profiler.installer; +import com.teamscale.profiler.installer.utils.MockTeamscale; import com.teamscale.profiler.installer.windows.WindowsRegistry; import okhttp3.HttpUrl; import org.junit.jupiter.api.AfterAll; @@ -15,6 +16,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import static com.teamscale.profiler.installer.utils.UninstallErrorReporterAssert.assertThat; import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ; import static org.assertj.core.api.Assertions.assertThat; @@ -98,8 +100,8 @@ void successfulInstallation() throws FatalInstallerError, IOException { void successfulUninstallation() throws FatalInstallerError { install(); Installer.UninstallerErrorReporter errorReporter = uninstall(); + assertThat(errorReporter).hadNoErrors(); - assertThat(errorReporter.wereErrorsReported()).isFalse(); assertThat(environmentFile).exists().content().isEqualTo(ENVIRONMENT_CONTENT); assertThat(systemdConfig).doesNotExist(); } @@ -109,8 +111,7 @@ void uninstallSuccessfullyEvenIfSystemDConfigWasManuallyRemoved() throws FatalIn install(); Files.delete(systemdConfig); Installer.UninstallerErrorReporter errorReporter = uninstall(); - - assertThat(errorReporter.wereErrorsReported()).isFalse(); + assertThat(errorReporter).hadNoErrors(); } @Test @@ -118,8 +119,7 @@ void uninstallSuccessfullyEvenIfEnvironmentFileDoesntExist() throws FatalInstall install(); Files.delete(environmentFile); Installer.UninstallerErrorReporter errorReporter = uninstall(); - - assertThat(errorReporter.wereErrorsReported()).isFalse(); + assertThat(errorReporter).hadNoErrors(); } @Test @@ -128,8 +128,8 @@ void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); Installer.UninstallerErrorReporter errorReporter = uninstall(); + assertThat(errorReporter).hadErrors(); - assertThat(errorReporter.wereErrorsReported()).isTrue(); assertThat(environmentFile).exists().content().isEqualTo(ENVIRONMENT_CONTENT); assertThat(systemdConfig).doesNotExist(); } @@ -140,8 +140,7 @@ void uninstallChangingEtcEnvironmentFails() throws FatalInstallerError { assertThat(environmentFile.toFile().setWritable(false, false)).isTrue(); Installer.UninstallerErrorReporter errorReporter = uninstall(); - - assertThat(errorReporter.wereErrorsReported()).isTrue(); + assertThat(errorReporter).hadErrors(); assertThat(environmentFile).exists().content().contains("_JAVA_OPTIONS"); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java index 1e72bcde3..9a1b5b98e 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java @@ -1,5 +1,8 @@ package com.teamscale.profiler.installer; +import com.teamscale.profiler.installer.utils.MockRegistry; +import com.teamscale.profiler.installer.utils.MockTeamscale; +import com.teamscale.profiler.installer.utils.UninstallErrorReporterAssert; import okhttp3.HttpUrl; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -75,8 +78,8 @@ void successfulInstallation() throws FatalInstallerError { void successfulUninstallation() throws FatalInstallerError { install(); Installer.UninstallerErrorReporter errorReporter = uninstall(); + UninstallErrorReporterAssert.assertThat(errorReporter).hadNoErrors(); - assertThat(errorReporter.wereErrorsReported()).isFalse(); assertThat(registry.getVariable("_JAVA_OPTIONS")).isNull(); assertThat(registry.getVariable("JAVA_TOOL_OPTIONS")).isNull(); } @@ -87,8 +90,8 @@ void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); Installer.UninstallerErrorReporter errorReporter = uninstall(); + UninstallErrorReporterAssert.assertThat(errorReporter).hadErrors(); - assertThat(errorReporter.wereErrorsReported()).isTrue(); assertThat(registry.getVariable("_JAVA_OPTIONS")).isNull(); assertThat(registry.getVariable("JAVA_TOOL_OPTIONS")).isNull(); } diff --git a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java index bcd0b4ed5..10155c72e 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/steps/InstallWindowsSystemEnvironmentStepTest.java @@ -3,7 +3,7 @@ import com.teamscale.profiler.installer.EnvironmentMap; import com.teamscale.profiler.installer.FatalInstallerError; import com.teamscale.profiler.installer.Installer; -import com.teamscale.profiler.installer.MockRegistry; +import com.teamscale.profiler.installer.utils.MockRegistry; import com.teamscale.profiler.installer.TeamscaleCredentials; import okhttp3.HttpUrl; import org.junit.jupiter.api.Test; diff --git a/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java b/installer/src/test/java/com/teamscale/profiler/installer/utils/MockRegistry.java similarity index 95% rename from installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java rename to installer/src/test/java/com/teamscale/profiler/installer/utils/MockRegistry.java index a95228009..43c6a4f8a 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/MockRegistry.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/utils/MockRegistry.java @@ -1,4 +1,4 @@ -package com.teamscale.profiler.installer; +package com.teamscale.profiler.installer.utils; import com.teamscale.profiler.installer.windows.IRegistry; import com.teamscale.profiler.installer.windows.WindowsRegistry; diff --git a/installer/src/test/java/com/teamscale/profiler/installer/MockTeamscale.java b/installer/src/test/java/com/teamscale/profiler/installer/utils/MockTeamscale.java similarity index 95% rename from installer/src/test/java/com/teamscale/profiler/installer/MockTeamscale.java rename to installer/src/test/java/com/teamscale/profiler/installer/utils/MockTeamscale.java index 9f793df19..cefc49966 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/MockTeamscale.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/utils/MockTeamscale.java @@ -1,4 +1,4 @@ -package com.teamscale.profiler.installer; +package com.teamscale.profiler.installer.utils; import spark.Request; import spark.Response; diff --git a/installer/src/test/java/com/teamscale/profiler/installer/utils/UninstallErrorReporterAssert.java b/installer/src/test/java/com/teamscale/profiler/installer/utils/UninstallErrorReporterAssert.java new file mode 100644 index 000000000..a2112085e --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/utils/UninstallErrorReporterAssert.java @@ -0,0 +1,31 @@ +package com.teamscale.profiler.installer.utils; + +import com.teamscale.profiler.installer.Installer; +import org.assertj.core.api.AbstractAssert; + +/** Assertions for {@link com.teamscale.profiler.installer.Installer.UninstallerErrorReporter} */ +public class UninstallErrorReporterAssert extends AbstractAssert { + + protected UninstallErrorReporterAssert(Installer.UninstallerErrorReporter uninstallerErrorReporter, + Class selfType) { + super(uninstallerErrorReporter, selfType); + } + + /** Asserts that no errors were reported. */ + public void hadNoErrors() { + if (actual.wereErrorsReported()) { + failWithMessage("Expected no errors to be reported during the uninstallation, but at least one occurred."); + } + } + + /** Asserts at least one error was reported. */ + public void hadErrors() { + if (!actual.wereErrorsReported()) { + failWithMessage("Unexpectedly, no errors were reported during the uninstallation."); + } + } + + public static UninstallErrorReporterAssert assertThat(Installer.UninstallerErrorReporter reporter) { + return new UninstallErrorReporterAssert(reporter, UninstallErrorReporterAssert.class); + } +} From 6c93f902a9fc3b8e28377215868aaf6aaef0bb30 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 09:42:10 +0100 Subject: [PATCH 14/62] add utility to mark directories as not writable during tests --- .../installer/AllPlatformsInstallerTest.java | 4 ++-- .../profiler/installer/LinuxInstallerTest.java | 5 +++-- .../profiler/installer/WindowsInstallerTest.java | 3 ++- .../profiler/installer/utils/TestUtils.java | 15 +++++++++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 installer/src/test/java/com/teamscale/profiler/installer/utils/TestUtils.java diff --git a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java index ea86e64f9..69b2e62fe 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/AllPlatformsInstallerTest.java @@ -2,6 +2,7 @@ import com.teamscale.profiler.installer.utils.MockRegistry; import com.teamscale.profiler.installer.utils.MockTeamscale; +import com.teamscale.profiler.installer.utils.TestUtils; import okhttp3.HttpUrl; import org.conqat.lib.commons.filesystem.FileSystemUtils; import org.junit.jupiter.api.AfterAll; @@ -101,8 +102,7 @@ void successfulUninstallation() throws FatalInstallerError { @Test void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { install(); - assertThat(targetDirectory.toFile().setWritable(false, false)) - .withFailMessage("Could not make target directory unwritable").isTrue(); + TestUtils.setPathWritable(targetDirectory, false); Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter).hadErrors(); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java index e453645eb..f5a2e8eae 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/LinuxInstallerTest.java @@ -1,6 +1,7 @@ package com.teamscale.profiler.installer; import com.teamscale.profiler.installer.utils.MockTeamscale; +import com.teamscale.profiler.installer.utils.TestUtils; import com.teamscale.profiler.installer.windows.WindowsRegistry; import okhttp3.HttpUrl; import org.junit.jupiter.api.AfterAll; @@ -125,7 +126,7 @@ void uninstallSuccessfullyEvenIfEnvironmentFileDoesntExist() throws FatalInstall @Test void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { install(); - assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); + TestUtils.setPathWritable(targetDirectory, false); Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter).hadErrors(); @@ -137,7 +138,7 @@ void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { @Test void uninstallChangingEtcEnvironmentFails() throws FatalInstallerError { install(); - assertThat(environmentFile.toFile().setWritable(false, false)).isTrue(); + TestUtils.setPathWritable(environmentFile, false); Installer.UninstallerErrorReporter errorReporter = uninstall(); assertThat(errorReporter).hadErrors(); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java b/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java index 9a1b5b98e..341fd1958 100644 --- a/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java +++ b/installer/src/test/java/com/teamscale/profiler/installer/WindowsInstallerTest.java @@ -2,6 +2,7 @@ import com.teamscale.profiler.installer.utils.MockRegistry; import com.teamscale.profiler.installer.utils.MockTeamscale; +import com.teamscale.profiler.installer.utils.TestUtils; import com.teamscale.profiler.installer.utils.UninstallErrorReporterAssert; import okhttp3.HttpUrl; import org.junit.jupiter.api.AfterAll; @@ -87,7 +88,7 @@ void successfulUninstallation() throws FatalInstallerError { @Test void uninstallDeletingAgentDirectoryFails() throws FatalInstallerError { install(); - assertThat(targetDirectory.toFile().setWritable(false, false)).isTrue(); + TestUtils.setPathWritable(targetDirectory, false); Installer.UninstallerErrorReporter errorReporter = uninstall(); UninstallErrorReporterAssert.assertThat(errorReporter).hadErrors(); diff --git a/installer/src/test/java/com/teamscale/profiler/installer/utils/TestUtils.java b/installer/src/test/java/com/teamscale/profiler/installer/utils/TestUtils.java new file mode 100644 index 000000000..a9c315672 --- /dev/null +++ b/installer/src/test/java/com/teamscale/profiler/installer/utils/TestUtils.java @@ -0,0 +1,15 @@ +package com.teamscale.profiler.installer.utils; + +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +/** Generic utilities for tests. */ +public class TestUtils { + + /** Changes the writability of the given path and asserts that the change was successful. */ + public static void setPathWritable(Path path, boolean shouldBeWritable) { + assertThat(path.toFile().setWritable(shouldBeWritable, shouldBeWritable)) + .withFailMessage("Failed to mark " + path + " as writable = " + shouldBeWritable).isTrue(); + } +} From d3e5f3a028b9fc97efba1e54019e8e1bcb511620 Mon Sep 17 00:00:00 2001 From: Fabian Streitel Date: Wed, 20 Dec 2023 11:16:25 +0100 Subject: [PATCH 15/62] convert to jlink build had to convert the installer to a Java 17 module and replace com.teamscale dependencies with Apache commons --- .idea/codeStyles/Project.xml | 1 + .idea/misc.xml | 3 +- agent/build.gradle.kts | 4 +- gradle/libs.versions.toml | 2 + installer/build.gradle.kts | 56 +++++++++++-------- .../profiler/installer/EnvironmentMap.java | 8 +++ .../profiler/installer/Installer.java | 8 +-- .../profiler/installer/RootCommand.java | 4 +- .../steps/InstallAgentFilesStep.java | 18 +++--- .../steps/InstallEtcEnvironmentStep.java | 22 ++++---- .../installer/steps/InstallSystemdStep.java | 19 +++---- .../InstallWindowsSystemEnvironmentStep.java | 6 +- installer/src/main/java/module-info.java | 10 ++++ .../installer/AllPlatformsInstallerTest.java | 11 ++-- settings.gradle.kts | 3 - 15 files changed, 99 insertions(+), 76 deletions(-) create mode 100644 installer/src/main/java/module-info.java diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index f6ceb4adf..f913e44d4 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -22,6 +22,7 @@ + -