This is an automated email from the ASF dual-hosted git repository. joshtynjala pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/royale-compiler.git
commit 6085daac2f73781f70c75ea57eebc8b31f8a6533 Author: Josh Tynjala <[email protected]> AuthorDate: Thu Jul 28 10:34:37 2022 -0700 royaleunit-ant-tasks: <royaleunit player=""/> attribute may be set to chromium, webkit, or firefox to test JS using Playwright Playwright allows for headless testing of JS in browsers in a more consistent environment than the local user's random installed web browser, including allowing testing with local file paths without security errors/warnings. If a custom executable is defined for the browser, that still takes precedence over the player attribute, same as before. --- royaleunit-ant-tasks/build.xml | 4 +- royaleunit-ant-tasks/pom.xml | 7 +- .../PlayerCommand.java => TestRunCommand.java} | 14 +- .../launcher/commands/TestRunCommandFactory.java | 108 +++++++++++++ .../commands/player/CustomPlayerCommand.java | 7 +- .../commands/player/DefaultPlayerCommand.java | 3 +- .../launcher/commands/player/PlayerCommand.java | 20 +-- .../commands/player/PlayerCommandFactory.java | 5 +- .../playwright/DefaultPlaywrightCommand.java | 177 +++++++++++++++++++++ .../PlaywrightCommand.java} | 20 +-- .../ProcessCommand.java} | 14 +- .../test/ant/launcher/contexts/DefaultContext.java | 36 +---- ...tContext.java => DefaultPlaywrightContext.java} | 19 ++- ...aultContext.java => DefaultProcessContext.java} | 17 +- .../ant/launcher/contexts/ExecutionContext.java | 16 +- .../launcher/contexts/ExecutionContextFactory.java | 47 +++++- .../ant/launcher/contexts/HeadlessContext.java | 8 +- ...ontext.java => PlaywrightExecutionContext.java} | 27 ++-- .../ProcessExecutionContext.java} | 28 ++-- .../org/apache/royale/test/ant/tasks/TestRun.java | 90 ++++++++--- .../ant/tasks/configuration/TaskConfiguration.java | 2 +- .../src/main/resources/downloads.xml | 56 ++++++- 22 files changed, 546 insertions(+), 179 deletions(-) diff --git a/royaleunit-ant-tasks/build.xml b/royaleunit-ant-tasks/build.xml index d7a7105cf..c33f07cca 100644 --- a/royaleunit-ant-tasks/build.xml +++ b/royaleunit-ant-tasks/build.xml @@ -47,7 +47,7 @@ <!-- Options for <javac> tasks --> <property name="javac.debug" value="true"/> <property name="javac.deprecation" value="false"/> - <property name="javac.src" value="1.6"/> + <property name="javac.src" value="1.8"/> <!-- JAR manifest entries --> <property name="manifest.sealed" value="false"/> @@ -112,7 +112,7 @@ <attribute name="Implementation-Title" value="${manifest.Implementation-Title}"/> <attribute name="Implementation-Version" value="${manifest.Implementation-Version}.${build.number}"/> <attribute name="Implementation-Vendor" value="${manifest.Implementation-Vendor}"/> - <attribute name="Class-Path" value="dom4j.jar java-websocket.jar slf4j-api.jar"/> + <attribute name="Class-Path" value="royaleunit/dom4j.jar royaleunit/java-websocket.jar royaleunit/slf4j-api.jar royaleunit/gson.jar royaleunit/driver.jar royaleunit/driver-bundle.jar royaleunit/playwright.jar"/> </manifest> <fileset dir="${royaleunittasks}/src/main/resources" includes="royaleUnitTasks.tasks"/> <fileset dir="${royaleunittasks}/src/main/resources" includes="TestRunner.template"/> diff --git a/royaleunit-ant-tasks/pom.xml b/royaleunit-ant-tasks/pom.xml index 0345de7c6..d810152d9 100644 --- a/royaleunit-ant-tasks/pom.xml +++ b/royaleunit-ant-tasks/pom.xml @@ -39,7 +39,7 @@ <archive> <manifestEntries> <!-- These paths are all defined the way the layout will be in the distribution --> - <Class-Path>dom4j.jar java-websocket.jar slf4j-api.jar</Class-Path> + <Class-Path>royaleunit/dom4j.jar royaleunit/java-websocket.jar royaleunit/slf4j-api.jar royaleunit/playwright.jar royaleunit/driver.jar royaleunit/driver-bundle.jar royaleunit/gson.jar</Class-Path> </manifestEntries> </archive> </configuration> @@ -78,6 +78,11 @@ <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> + <dependency> + <groupId>com.microsoft.playwright</groupId> + <artifactId>playwright</artifactId> + <version>1.23.0</version> + </dependency> </dependencies> <properties /></project> diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/TestRunCommand.java similarity index 72% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/TestRunCommand.java index 157c19817..92b16936c 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/TestRunCommand.java @@ -14,25 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.royale.test.ant.launcher.commands.player; +package org.apache.royale.test.ant.launcher.commands; import java.io.File; -import java.io.IOException; import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Execute; /** - * Class used to abstract an extension of {@link Execute} that has its own handle to a {@link Project} - * and can setup context for using the project + * Class that has its own handle to a {@link Project} and can setup context for + * using the project */ -public interface PlayerCommand +public interface TestRunCommand { public void setProject(Project project); public void setEnvironment(String[] variables); - public File getFileToExecute(); - public void setSwf(File swf); public void setUrl(String url); + public void setSwf(File swf); public void prepare(); - public Process launch() throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/TestRunCommandFactory.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/TestRunCommandFactory.java new file mode 100644 index 000000000..4dc9239bb --- /dev/null +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/TestRunCommandFactory.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.royale.test.ant.launcher.commands; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import org.apache.royale.test.ant.launcher.OperatingSystem; +import org.apache.royale.test.ant.launcher.commands.player.AdlCommand; +import org.apache.royale.test.ant.launcher.commands.player.CustomPlayerCommand; +import org.apache.royale.test.ant.launcher.commands.player.DefaultPlayerCommand; +import org.apache.royale.test.ant.launcher.commands.player.FlashPlayerCommand; +import org.apache.royale.test.ant.launcher.commands.playwright.DefaultPlaywrightCommand; +import org.apache.royale.test.ant.launcher.commands.playwright.PlaywrightCommand; +import org.apache.royale.test.ant.launcher.platforms.LinuxDefaults; +import org.apache.royale.test.ant.launcher.platforms.MacOSXDefaults; +import org.apache.royale.test.ant.launcher.platforms.WindowsDefaults; + +public class TestRunCommandFactory +{ + private static final List<String> VALID_PLAYWRIGHT_PLAYERS = Arrays.asList(new String[]{"html", "chromium", "firefox", "webkit"}); + + /** + * Factory method to create the appropriate player and provide it with a set of defaults for + * the executing platform. + * + * @param os + * @param player "flash" or "air" + * @param customCommand + * @param customCommandArgs + * @param localTrusted + * @return Desired player command with platform defaults possibly wrapped in a custom command + */ + public static TestRunCommand createCommand(OperatingSystem os, + String player, File customCommand, String[] customCommandArgs, + boolean localTrusted) + { + TestRunCommand newInstance = null; + + //choose runtime + if (customCommand == null && VALID_PLAYWRIGHT_PLAYERS.contains(player)) + { + PlaywrightCommand playwrightCommand = new DefaultPlaywrightCommand(); + playwrightCommand.setBrowser(player); + newInstance = playwrightCommand; + } + else + { + DefaultPlayerCommand defaultInstance = null; + + if (!player.equals("air")) + { + FlashPlayerCommand fpCommand = new FlashPlayerCommand(); + fpCommand.setLocalTrusted(localTrusted); + defaultInstance = fpCommand; + } + else + { + defaultInstance = new AdlCommand(); + } + + //set defaults + if (os.equals(OperatingSystem.WINDOWS)) + { + defaultInstance.setDefaults(new WindowsDefaults()); + } + else if(os.equals(OperatingSystem.MACOSX)) + { + defaultInstance.setDefaults(new MacOSXDefaults()); + } + else + { + defaultInstance.setDefaults(new LinuxDefaults()); + } + + //if a custom command has been provided, use it to wrap the default command + if(customCommand != null) + { + CustomPlayerCommand customInstance = new CustomPlayerCommand(); + customInstance.setProxiedCommand(defaultInstance); + customInstance.setExecutable(customCommand); + customInstance.setExecutableArgs(customCommandArgs); + newInstance = customInstance; + } + else + { + newInstance = defaultInstance; + } + } + + return newInstance; + } +} diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/CustomPlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/CustomPlayerCommand.java index ebf6e80dd..dc2848fab 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/CustomPlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/CustomPlayerCommand.java @@ -20,17 +20,18 @@ import java.io.File; import java.io.IOException; import java.util.Vector; +import org.apache.royale.test.ant.LoggingUtil; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Execute; -import org.apache.royale.test.ant.LoggingUtil; -public class CustomPlayerCommand implements PlayerCommand +public class CustomPlayerCommand implements ProcessCommand { private DefaultPlayerCommand proxiedCommand; private File executable; private String[] executableArgs; - public PlayerCommand getProxiedCommand() + public ProcessCommand getProxiedCommand() { return proxiedCommand; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/DefaultPlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/DefaultPlayerCommand.java index 92d0a83d9..2be921cbd 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/DefaultPlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/DefaultPlayerCommand.java @@ -20,9 +20,10 @@ import java.io.File; import java.io.IOException; import org.apache.royale.test.ant.launcher.commands.Command; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; import org.apache.royale.test.ant.launcher.platforms.PlatformDefaults; -public abstract class DefaultPlayerCommand extends Command implements PlayerCommand +public abstract class DefaultPlayerCommand extends Command implements ProcessCommand { private String url; private File swf; diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java index 157c19817..55e700f5b 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java @@ -16,23 +16,9 @@ */ package org.apache.royale.test.ant.launcher.commands.player; -import java.io.File; -import java.io.IOException; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Execute; - -/** - * Class used to abstract an extension of {@link Execute} that has its own handle to a {@link Project} - * and can setup context for using the project - */ -public interface PlayerCommand +@Deprecated +public interface PlayerCommand extends ProcessCommand { - public void setProject(Project project); - public void setEnvironment(String[] variables); - public File getFileToExecute(); - public void setSwf(File swf); - public void setUrl(String url); - public void prepare(); - public Process launch() throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommandFactory.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommandFactory.java index 8c3264f2f..c8857aca4 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommandFactory.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommandFactory.java @@ -19,6 +19,7 @@ package org.apache.royale.test.ant.launcher.commands.player; import java.io.File; import org.apache.royale.test.ant.launcher.OperatingSystem; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; import org.apache.royale.test.ant.launcher.platforms.LinuxDefaults; import org.apache.royale.test.ant.launcher.platforms.MacOSXDefaults; import org.apache.royale.test.ant.launcher.platforms.WindowsDefaults; @@ -35,9 +36,9 @@ public class PlayerCommandFactory * @param localTrusted * @return Desired player command with platform defaults possibly wrapped in a custom command */ - public static PlayerCommand createPlayer(OperatingSystem os, String player, File customCommand, String[] customCommandArgs, boolean localTrusted) + public static ProcessCommand createPlayer(OperatingSystem os, String player, File customCommand, String[] customCommandArgs, boolean localTrusted) { - PlayerCommand newInstance = null; + ProcessCommand newInstance = null; DefaultPlayerCommand defaultInstance = null; diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/playwright/DefaultPlaywrightCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/playwright/DefaultPlaywrightCommand.java new file mode 100644 index 000000000..82b862dfc --- /dev/null +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/playwright/DefaultPlaywrightCommand.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.royale.test.ant.launcher.commands.playwright; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import java.util.function.Consumer; + +import org.apache.royale.test.ant.LoggingUtil; +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Execute; + +import com.microsoft.playwright.Browser; +import com.microsoft.playwright.BrowserType; +import com.microsoft.playwright.ConsoleMessage; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.Playwright; +import com.microsoft.playwright.Playwright.CreateOptions; +import com.microsoft.playwright.impl.PlaywrightImpl; + +public class DefaultPlaywrightCommand implements PlaywrightCommand +{ + private Project project; + private String url; + private File swf; + private String browser; + private String[] environment; + private Playwright playwright; + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public Project getProject() + { + return project; + } + + public void setProject(Project project) + { + this.project = project; + } + + public String getUrl() + { + return url; + } + + public void setUrl(String url) + { + this.url = url; + } + + public File getSwf() + { + return swf; + } + + public void setSwf(File swf) + { + this.swf = swf; + } + + public void prepare() + { + ((AntClassLoader)getClass().getClassLoader()).setThreadContextLoader(); + + CreateOptions createOptions = new CreateOptions(); + createOptions.setEnv(getJointEnvironment()); + playwright = PlaywrightImpl.create(createOptions); + } + + public Playwright launch() throws IOException + { + LoggingUtil.log("Executing Playwright with " + browser); + BrowserType browserType = null; + switch (browser) + { + case "html": + case "chromium": + { + browserType = playwright.chromium(); + break; + } + case "webkit": + { + browserType = playwright.webkit(); + break; + } + case "firefox": + { + browserType = playwright.firefox(); + break; + } + default: + throw new IOException("Unknown browser: " + browser); + } + Browser browserInstance = browserType.launch(); + Page page = browserInstance.newPage(); + page.onConsoleMessage(new Consumer<ConsoleMessage>() + { + @Override + public void accept(ConsoleMessage t) + { + switch (t.type()) + { + case "error": + LoggingUtil.error(t.text()); + break; + default: + LoggingUtil.log(t.text()); + } + } + }); + + if (getUrl() != null) + { + page.navigate(getUrl()); + } + else + { + page.navigate(swf.toURI().toString()); + } + return playwright; + } + + public void setEnvironment(String[] variables) + { + environment = variables; + } + + /** + * Combine process environment variables and command's environment to emulate the default + * behavior of the Execute task. Needed especially when user expects environment to be + * available to custom command (e.g. - xvnc with player not on path). + */ + @SuppressWarnings("unchecked") + private Map<String,String> getJointEnvironment() + { + Map<String, String> jointEnvironment = new HashMap<String, String>(); + Vector<String> executeProcEnvironment = Execute.getProcEnvironment(); + String[] procEnvironment = executeProcEnvironment.toArray(new String[0]); + for (String envVar : procEnvironment) + { + String[] parts = envVar.split("="); + jointEnvironment.put(parts[0].trim(), parts.length > 1 ? parts[1].trim() : ""); + } + if (environment != null) + { + for (String envVar : environment) + { + String[] parts = envVar.split("="); + jointEnvironment.put(parts[0].trim(), parts.length > 1 ? parts[1].trim() : ""); + } + } + return jointEnvironment; + } +} diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/playwright/PlaywrightCommand.java similarity index 63% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/playwright/PlaywrightCommand.java index 157c19817..d843b71b5 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/playwright/PlaywrightCommand.java @@ -14,25 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.royale.test.ant.launcher.commands.player; +package org.apache.royale.test.ant.launcher.commands.playwright; -import java.io.File; import java.io.IOException; +import org.apache.royale.test.ant.launcher.commands.TestRunCommand; import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Execute; + +import com.microsoft.playwright.Playwright; /** - * Class used to abstract an extension of {@link Execute} that has its own handle to a {@link Project} + * Class used to abstract {@link Playwright} that has its own handle to a {@link Project} * and can setup context for using the project */ -public interface PlayerCommand +public interface PlaywrightCommand extends TestRunCommand { - public void setProject(Project project); - public void setEnvironment(String[] variables); - public File getFileToExecute(); - public void setSwf(File swf); - public void setUrl(String url); - public void prepare(); - public Process launch() throws IOException; + public void setBrowser(String browser); + public Playwright launch() throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/process/ProcessCommand.java similarity index 74% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/process/ProcessCommand.java index 157c19817..5f3f2bf38 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/process/ProcessCommand.java @@ -14,25 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.royale.test.ant.launcher.commands.player; +package org.apache.royale.test.ant.launcher.commands.process; import java.io.File; import java.io.IOException; +import org.apache.royale.test.ant.launcher.commands.TestRunCommand; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Execute; /** - * Class used to abstract an extension of {@link Execute} that has its own handle to a {@link Project} - * and can setup context for using the project + * Class used to abstract an extension of {@link Execute} that has its own + * handle to a {@link Project} and can setup context for using the project */ -public interface PlayerCommand +public interface ProcessCommand extends TestRunCommand { - public void setProject(Project project); - public void setEnvironment(String[] variables); public File getFileToExecute(); - public void setSwf(File swf); - public void setUrl(String url); - public void prepare(); public Process launch() throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java index 412eb353d..e5feaba91 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java @@ -16,39 +16,7 @@ */ package org.apache.royale.test.ant.launcher.contexts; -import java.io.IOException; - -import org.apache.tools.ant.Project; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; - -public class DefaultContext implements ExecutionContext +@Deprecated +public class DefaultContext extends DefaultProcessContext { - private PlayerCommand command; - - @SuppressWarnings("unused") - private Project project; - - public void setProject(Project project) - { - this.project = project; - } - public void setCommand(PlayerCommand command) - { - this.command = command; - } - - public void start() throws IOException - { - //prep anything the command needs to run - command.prepare(); - } - - public void stop(Process playerProcess) throws IOException - { - //destroy the process related to the player if it exists - if(playerProcess != null) - { - playerProcess.destroy(); - } - } } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultPlaywrightContext.java similarity index 73% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultPlaywrightContext.java index 412eb353d..0f5ba7d73 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultPlaywrightContext.java @@ -18,12 +18,14 @@ package org.apache.royale.test.ant.launcher.contexts; import java.io.IOException; +import org.apache.royale.test.ant.launcher.commands.playwright.PlaywrightCommand; import org.apache.tools.ant.Project; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; -public class DefaultContext implements ExecutionContext +import com.microsoft.playwright.Playwright; + +public class DefaultPlaywrightContext implements PlaywrightExecutionContext { - private PlayerCommand command; + private PlaywrightCommand command; @SuppressWarnings("unused") private Project project; @@ -32,7 +34,8 @@ public class DefaultContext implements ExecutionContext { this.project = project; } - public void setCommand(PlayerCommand command) + + public void setCommand(PlaywrightCommand command) { this.command = command; } @@ -43,12 +46,12 @@ public class DefaultContext implements ExecutionContext command.prepare(); } - public void stop(Process playerProcess) throws IOException + public void stop(Playwright playwright) throws IOException { - //destroy the process related to the player if it exists - if(playerProcess != null) + //destroy the playwright instance, if it exists + if(playwright != null) { - playerProcess.destroy(); + playwright.close(); } } } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultProcessContext.java similarity index 75% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultProcessContext.java index 412eb353d..3b0bc80d5 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultContext.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/DefaultProcessContext.java @@ -18,12 +18,12 @@ package org.apache.royale.test.ant.launcher.contexts; import java.io.IOException; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; import org.apache.tools.ant.Project; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; -public class DefaultContext implements ExecutionContext +public class DefaultProcessContext implements ProcessExecutionContext { - private PlayerCommand command; + private ProcessCommand command; @SuppressWarnings("unused") private Project project; @@ -32,7 +32,8 @@ public class DefaultContext implements ExecutionContext { this.project = project; } - public void setCommand(PlayerCommand command) + + public void setCommand(ProcessCommand command) { this.command = command; } @@ -43,12 +44,12 @@ public class DefaultContext implements ExecutionContext command.prepare(); } - public void stop(Process playerProcess) throws IOException + public void stop(Process process) throws IOException { - //destroy the process related to the player if it exists - if(playerProcess != null) + //destroy the process related to the runtime, if it exists + if(process != null) { - playerProcess.destroy(); + process.destroy(); } } } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java index 1b5c97d66..6af60f089 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java @@ -19,29 +19,19 @@ package org.apache.royale.test.ant.launcher.contexts; import java.io.IOException; import org.apache.tools.ant.Project; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; /** - * Basis for executing a player command. + * Basis for executing a test run command. */ public interface ExecutionContext { public void setProject(Project project); - public void setCommand(PlayerCommand command); /** - * Starts the execution context and any work associated with the individual implementations. + * Starts the execution context and any work associated with the individual + * implementations. * * @throws IOException */ public void start() throws IOException; - - /** - * Stops the execution context and manages the player Process as well as any work associated - * with the individual implementations. - * - * @param playerProcess - * @throws IOException - */ - public void stop(Process playerProcess) throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContextFactory.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContextFactory.java index 89be35dfd..4cf7c5ae2 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContextFactory.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContextFactory.java @@ -17,6 +17,9 @@ package org.apache.royale.test.ant.launcher.contexts; import org.apache.royale.test.ant.launcher.OperatingSystem; +import org.apache.royale.test.ant.launcher.commands.TestRunCommand; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; +import org.apache.royale.test.ant.launcher.commands.playwright.PlaywrightCommand; public class ExecutionContextFactory { @@ -41,9 +44,51 @@ public class ExecutionContextFactory } else { - context = new DefaultContext(); + context = new DefaultProcessContext(); } + return context; + } + + /** + * Used to generate new instances of an execution context based on the test + * run command, the OS, and whether the build should run headlessly. + * + * @param os Current OS. + * @param headless Should the build run headlessly. + * @param display The vnc display number to use if headless + * @param command The test run command the context is for + * + * @return + */ + public static ExecutionContext createContext(TestRunCommand command, + OperatingSystem os, boolean headless, int display) + { + ExecutionContext context = null; + + if (command instanceof PlaywrightCommand) + { + PlaywrightExecutionContext playwrightContext = new DefaultPlaywrightContext(); + playwrightContext.setCommand((PlaywrightCommand)command); + context = playwrightContext; + } + else + { + ProcessExecutionContext processContext = null; + boolean trulyHeadless = headless && (os == OperatingSystem.LINUX); + if(trulyHeadless) + { + processContext = new HeadlessContext(display); + } + else + { + processContext = new DefaultProcessContext(); + } + processContext.setCommand((ProcessCommand)command); + context = processContext; + } + + return context; } } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/HeadlessContext.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/HeadlessContext.java index d18abe35d..21fc11682 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/HeadlessContext.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/HeadlessContext.java @@ -23,15 +23,15 @@ import org.apache.royale.test.ant.LoggingUtil; import org.apache.royale.test.ant.launcher.commands.headless.XvncException; import org.apache.royale.test.ant.launcher.commands.headless.XvncStartCommand; import org.apache.royale.test.ant.launcher.commands.headless.XvncStopCommand; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; /** * Context used to wrap a call to the player command in a start and stop of a vncserver. * All vncserver commands are blocking. */ -public class HeadlessContext implements ExecutionContext +public class HeadlessContext implements ProcessExecutionContext { - private PlayerCommand playerCommand; + private ProcessCommand playerCommand; private int startDisplay; private int finalDisplay; private Project project; @@ -46,7 +46,7 @@ public class HeadlessContext implements ExecutionContext this.project = project; } - public void setCommand(PlayerCommand command) + public void setCommand(ProcessCommand command) { this.playerCommand = command; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/PlaywrightExecutionContext.java similarity index 59% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/PlaywrightExecutionContext.java index 1b5c97d66..50635480b 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ExecutionContext.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/PlaywrightExecutionContext.java @@ -18,30 +18,23 @@ package org.apache.royale.test.ant.launcher.contexts; import java.io.IOException; -import org.apache.tools.ant.Project; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; +import org.apache.royale.test.ant.launcher.commands.playwright.PlaywrightCommand; + +import com.microsoft.playwright.Playwright; /** - * Basis for executing a player command. + * Basis for executing a Playwright command. */ -public interface ExecutionContext +public interface PlaywrightExecutionContext extends ExecutionContext { - public void setProject(Project project); - public void setCommand(PlayerCommand command); - - /** - * Starts the execution context and any work associated with the individual implementations. - * - * @throws IOException - */ - public void start() throws IOException; - + public void setCommand(PlaywrightCommand command); + /** - * Stops the execution context and manages the player Process as well as any work associated + * Stops the execution context and manages the Playwright instnace as well as any work associated * with the individual implementations. * - * @param playerProcess + * @param playwright * @throws IOException */ - public void stop(Process playerProcess) throws IOException; + public void stop(Playwright playwright) throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ProcessExecutionContext.java similarity index 57% copy from royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java copy to royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ProcessExecutionContext.java index 157c19817..10851462a 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/commands/player/PlayerCommand.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/launcher/contexts/ProcessExecutionContext.java @@ -14,25 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.royale.test.ant.launcher.commands.player; +package org.apache.royale.test.ant.launcher.contexts; -import java.io.File; import java.io.IOException; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Execute; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; /** - * Class used to abstract an extension of {@link Execute} that has its own handle to a {@link Project} - * and can setup context for using the project + * Basis for executing a process command. */ -public interface PlayerCommand +public interface ProcessExecutionContext extends ExecutionContext { - public void setProject(Project project); - public void setEnvironment(String[] variables); - public File getFileToExecute(); - public void setSwf(File swf); - public void setUrl(String url); - public void prepare(); - public Process launch() throws IOException; + public void setCommand(ProcessCommand command); + + /** + * Stops the execution context and manages the Process as well as any work + * associated with the individual implementations. + * + * @param process + * @throws IOException + */ + public void stop(Process process) throws IOException; } diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/TestRun.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/TestRun.java index d46e03beb..350397a12 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/TestRun.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/TestRun.java @@ -21,20 +21,27 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; +import org.apache.royale.test.ant.IRoyaleUnitServer; +import org.apache.royale.test.ant.LoggingUtil; import org.apache.royale.test.ant.RoyaleUnitSocketServer; import org.apache.royale.test.ant.RoyaleUnitSocketThread; import org.apache.royale.test.ant.RoyaleUnitWebSocketServer; -import org.apache.royale.test.ant.IRoyaleUnitServer; -import org.apache.royale.test.ant.LoggingUtil; +import org.apache.royale.test.ant.launcher.commands.TestRunCommand; +import org.apache.royale.test.ant.launcher.commands.TestRunCommandFactory; import org.apache.royale.test.ant.launcher.commands.player.AdlCommand; -import org.apache.royale.test.ant.launcher.commands.player.PlayerCommand; import org.apache.royale.test.ant.launcher.commands.player.PlayerCommandFactory; +import org.apache.royale.test.ant.launcher.commands.playwright.PlaywrightCommand; +import org.apache.royale.test.ant.launcher.commands.process.ProcessCommand; import org.apache.royale.test.ant.launcher.contexts.ExecutionContext; import org.apache.royale.test.ant.launcher.contexts.ExecutionContextFactory; +import org.apache.royale.test.ant.launcher.contexts.PlaywrightExecutionContext; +import org.apache.royale.test.ant.launcher.contexts.ProcessExecutionContext; import org.apache.royale.test.ant.report.Reports; import org.apache.royale.test.ant.tasks.configuration.TestRunConfiguration; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; + +import com.microsoft.playwright.Playwright; public class TestRun { @@ -58,18 +65,27 @@ public class TestRun try { - // setup daemon - Future<Object> daemon = setupSocketThread(); - - // run the execution context and player - PlayerCommand player = obtainPlayer(); - ExecutionContext context = obtainContext(player); + // run the execution context and runtime + TestRunCommand command = obtainTestRunCommand(); + ExecutionContext context = obtainContext(command); //start the execution context context.start(); + + // setup daemon + Future<Object> daemon = setupSocketThread(); - //launch the player - Process process = player.launch(); + //launch the runtime + Process process = null; + Playwright playwright = null; + if (command instanceof ProcessCommand) + { + process = ((ProcessCommand)command).launch(); + } + else if (command instanceof PlaywrightCommand) + { + playwright = ((PlaywrightCommand)command).launch(); + } try { @@ -79,7 +95,14 @@ public class TestRun finally { //stop the execution context now that socket thread is done - context.stop(process); + if (context instanceof ProcessExecutionContext) + { + ((ProcessExecutionContext)context).stop(process); + } + else if (context instanceof PlaywrightExecutionContext) + { + ((PlaywrightExecutionContext)context).stop(playwright); + } } // print summaries and check for failure @@ -97,10 +120,10 @@ public class TestRun * * @return PlayerCommand based on user config */ - protected PlayerCommand obtainPlayer() + protected ProcessCommand obtainPlayer() { // get command from factory - PlayerCommand command = PlayerCommandFactory.createPlayer( + ProcessCommand command = PlayerCommandFactory.createPlayer( configuration.getOs(), configuration.getPlayer(), configuration.getCommand(), @@ -119,20 +142,47 @@ public class TestRun return command; } + /** + * Fetch the test run command to execute the tests. + * + * @return TestRunCommand based on user config + */ + protected TestRunCommand obtainTestRunCommand() + { + // get command from factory + TestRunCommand command = TestRunCommandFactory.createCommand( + configuration.getOs(), + configuration.getPlayer(), + configuration.getCommand(), + configuration.getCommandArgs(), + configuration.isLocalTrusted()); + + command.setProject(project); + command.setSwf(configuration.getSwf()); + command.setUrl(configuration.getUrl()); + + if(command instanceof AdlCommand) + { + ((AdlCommand)command).setPrecompiledAppDescriptor(configuration.getPrecompiledAppDescriptor()); + } + + return command; + } + /** * * @param player PlayerCommand which should be executed * @return Context to wrap the execution of the PlayerCommand */ - protected ExecutionContext obtainContext(PlayerCommand player) + protected ExecutionContext obtainContext(TestRunCommand command) { ExecutionContext context = ExecutionContextFactory.createContext( + command, configuration.getOs(), configuration.isHeadless(), configuration.getDisplay()); - + context.setProject(project); - context.setCommand(player); return context; } @@ -147,7 +197,7 @@ public class TestRun // Create server for use by thread IRoyaleUnitServer server = null; - if(configuration.getPlayer().equals("html")) + if(!"air".equals(configuration.getPlayer()) && !"flash".equals(configuration.getPlayer())) { server = new RoyaleUnitWebSocketServer( configuration.getPort(), configuration.getSocketTimeout()); diff --git a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/configuration/TaskConfiguration.java b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/configuration/TaskConfiguration.java index 69edba135..4cae11bde 100644 --- a/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/configuration/TaskConfiguration.java +++ b/royaleunit-ant-tasks/src/main/java/org/apache/royale/test/ant/tasks/configuration/TaskConfiguration.java @@ -32,7 +32,7 @@ public class TaskConfiguration { private final String DEFAULT_WORKING_PATH = "."; private final String DEFAULT_REPORT_PATH = "."; - private final List<String> VALID_PLAYERS = Arrays.asList(new String[]{"flash", "air", "html"}); + private final List<String> VALID_PLAYERS = Arrays.asList(new String[]{"flash", "air", "html", "chromium", "firefox", "webkit"}); private String player = "flash"; private File reportDir = null; diff --git a/royaleunit-ant-tasks/src/main/resources/downloads.xml b/royaleunit-ant-tasks/src/main/resources/downloads.xml index 73e8451b4..44863f2f5 100644 --- a/royaleunit-ant-tasks/src/main/resources/downloads.xml +++ b/royaleunit-ant-tasks/src/main/resources/downloads.xml @@ -80,12 +80,62 @@ <param name="src.folder" value="dom4j/dom4j/${dom4j.version}"/> <param name="src.filename" value="dom4j-${dom4j.version}.jar"/> <param name="src.checksum" value="4d8f51d3fe3900efc6e395be48030d6d"/> - <param name="dest.folder" value=""/> + <param name="dest.folder" value="royaleunit"/> <param name="dest.filename" value="${dom4j.name}.jar"/> <param name="license.use.url" value="https://raw.githubusercontent.com/dom4j/dom4j/master/LICENSE"/> <param name="license.cacheName" value="dom4j-LICENSE.txt"/> </antcall> + <!-- Playwright --> + <property name="gson.name" value="gson"/> + <property name="gson.version" value="2.9.0"/> + <antcall target="download-dependency"> + <param name="name" value="${gson.name}"/> + <param name="src.server" value="${maven.search.url}"/> + <param name="src.folder" value="com/google/code/gson/gson/${gson.version}"/> + <param name="src.filename" value="gson-${gson.version}.jar"/> + <param name="src.checksum" value="53fa3e6753e90d931d62cb89580fde2f"/> + <param name="dest.folder" value="royaleunit"/> + <param name="dest.filename" value="${gson.name}.jar"/> + <param name="license.use.url" value="https://raw.githubusercontent.com/Google/gson/master/LICENSE"/> + <param name="license.cacheName" value="${gson.name}-LICENSE.txt"/> + </antcall> + + <!-- Playwright --> + <property name="playwright.name" value="playwright"/> + <property name="playwright.version" value="1.23.0"/> + <antcall target="download-dependency"> + <param name="name" value="${playwright.name}"/> + <param name="src.server" value="${maven.search.url}"/> + <param name="src.folder" value="com/microsoft/playwright/playwright/${playwright.version}"/> + <param name="src.filename" value="playwright-${playwright.version}.jar"/> + <param name="src.checksum" value="9e6ef31572b5ac7e21ff7937ce1c1a82"/> + <param name="dest.folder" value="royaleunit"/> + <param name="dest.filename" value="${playwright.name}.jar"/> + <param name="license.use.url" value="https://raw.githubusercontent.com/Microsoft/playwright-java/main/LICENSE"/> + <param name="license.cacheName" value="${playwright.name}-LICENSE.txt"/> + </antcall> + <property name="playwrightdriver.name" value="driver"/> + <antcall target="download-dependency"> + <param name="name" value="${playwrightdriver.name}"/> + <param name="src.server" value="${maven.search.url}"/> + <param name="src.folder" value="com/microsoft/playwright/driver/${playwright.version}"/> + <param name="src.filename" value="driver-${playwright.version}.jar"/> + <param name="src.checksum" value="1d433a1eee72d15f96fbffc2b400813c"/> + <param name="dest.folder" value="royaleunit"/> + <param name="dest.filename" value="${playwrightdriver.name}.jar"/> + </antcall> + <property name="playwrightdriverbundle.name" value="driver-bundle"/> + <antcall target="download-dependency"> + <param name="name" value="${playwrightdriverbundle.name}"/> + <param name="src.server" value="${maven.search.url}"/> + <param name="src.folder" value="com/microsoft/playwright/driver-bundle/${playwright.version}"/> + <param name="src.filename" value="driver-bundle-${playwright.version}.jar"/> + <param name="src.checksum" value="080af38649fb1dca134c3c459bb78eb9"/> + <param name="dest.folder" value="royaleunit"/> + <param name="dest.filename" value="${playwrightdriverbundle.name}.jar"/> + </antcall> + <!-- Java-WebSocket --> <property name="javawebsocket.name" value="java-websocket"/> <property name="javawebsocket.version" value="1.4.0"/> @@ -95,7 +145,7 @@ <param name="src.folder" value="org/java-websocket/Java-WebSocket/${javawebsocket.version}"/> <param name="src.filename" value="Java-WebSocket-${javawebsocket.version}.jar"/> <param name="src.checksum" value="59c1134b8c50ace9e074e9f1d5da4aaa"/> - <param name="dest.folder" value=""/> + <param name="dest.folder" value="royaleunit"/> <param name="dest.filename" value="${javawebsocket.name}.jar"/> <param name="license.use.url" value="https://raw.githubusercontent.com/TooTallNate/Java-WebSocket/master/LICENSE"/> <param name="license.cacheName" value="${javawebsocket.name}-LICENSE.txt"/> @@ -110,7 +160,7 @@ <param name="src.folder" value="org/slf4j/slf4j-api/${slf4j.version}"/> <param name="src.filename" value="slf4j-api-${slf4j.version}.jar"/> <param name="src.checksum" value="caafe376afb7086dcbee79f780394ca3"/> - <param name="dest.folder" value=""/> + <param name="dest.folder" value="royaleunit"/> <param name="dest.filename" value="${slf4j.name}.jar"/> <param name="license.use.url" value="https://raw.githubusercontent.com/qos-ch/slf4j/master/LICENSE.txt"/> <param name="license.cacheName" value="${slf4j.name}-LICENSE.txt"/>
