This is an automated email from the ASF dual-hosted git repository. liuxun pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/submarine.git
The following commit(s) were added to refs/heads/master by this push: new e8d6727 SUBMARINE-338. Enable integration tests in travis. e8d6727 is described below commit e8d6727b9a00f3c8a157a935e7cf5dde465814e0 Author: Zac Zhou <zhouq...@apache.org> AuthorDate: Fri Jan 10 20:39:29 2020 +0800 SUBMARINE-338. Enable integration tests in travis. ### What is this PR for? There are some issues in current integration tests. 1. Submarine server has used angular frontend already by default. The integration tests are supposed to adjust according to this change. 2. To run integration tests, we need to install firefox browser and start submarine server in advance. 3. To run integration tests, we need to use the command of "mvn verify" instead of "mvn test". ### What type of PR is it? Improvement ### What is the Jira issue? https://issues.apache.org/jira/browse/SUBMARINE-338 ### How should this be tested? https://travis-ci.org/yuanzac/hadoop-submarine/builds/635242563?utm_source=github_status&utm_medium=notification ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Zac Zhou <zhouq...@apache.org> Closes #146 from yuanzac/topic/SUBMARINE-338 and squashes the following commits: 2696e75 [Zac Zhou] SUBMARINE-338. Refactor e2e code. 870e724 [Zac Zhou] SUBMARINE-338. Enable integration tests in travis. --- .travis.yml | 24 +- pom.xml | 4 +- submarine-test/e2e/pom.xml | 95 +++++--- .../org/apache/submarine/AbstractSubmarineIT.java | 155 ++++++++++--- .../apache/submarine/ChromeWebDriverProvider.java | 148 ++++++++++++ .../java/org/apache/submarine/CommandExecutor.java | 92 +++++--- .../apache/submarine/FirefoxWebDriverProvider.java | 152 ++++++++++++ .../java/org/apache/submarine/ProcessData.java | 257 +++++++++++++++++++++ .../org/apache/submarine/SubmarineITUtils.java | 60 +++++ .../org/apache/submarine/WebDriverManager.java | 182 +++++++-------- .../org/apache/submarine/WebDriverProvider.java | 42 ++++ .../org/apache/submarine/integration/loginIT.java | 18 +- 12 files changed, 1028 insertions(+), 201 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75000b8..58cf9dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,8 @@ addons: services: - mysql + # https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI + - xvfb env: global: @@ -47,6 +49,7 @@ env: - EXCLUDE_SUBMODULE_TONY="!submodules/tony,!submodules/tony/tony-mini,!submodules/tony/tony-core,!submodules/tony/tony-proxy,!submodules/tony/tony-portal,!submodules/tony/tony-azkaban,!submodules/tony/tony-cli" - EXCLUDE_K8S="!submarine-server/server-submitter/submitter-k8s" - EXCLUDE_COMMON_RPC="!submarine-commons/commons-rpc" + - MOZ_HEADLESS=1 before_install: # maven 3.6.1 (3.6.2 build tony failed!!!) @@ -65,6 +68,15 @@ before_install: - mysql -e "CREATE USER 'metastore_test'@'%' IDENTIFIED BY 'password_test';" - mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'metastore_test'@'%';" - mysql -e "use metastore_test; source ./docs/database/metastore.sql; show tables;" + # For e2e tests + - mysql -e "create database submarine;" + - mysql -e "CREATE USER 'submarine'@'%' IDENTIFIED BY 'password';" + - mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'submarine'@'%';" + - mysql -e "use submarine; source ./docs/database/submarine.sql; source ./docs/database/submarine-data.sql; show tables;" + - mysql -e "create database metastore;" + - mysql -e "CREATE USER 'metastore'@'%' IDENTIFIED BY 'password';" + - mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'metastore'@'%';" + - mysql -e "use metastore; source ./docs/database/metastore.sql; show tables;" - ./dev-support/travis/install_external_dependencies.sh # protobuf 3.10.1 - PROTOBUF_VERSION=3.10.1 @@ -156,7 +168,9 @@ matrix: language: java jdk: "openjdk8" dist: xenial - env: PROFILE="-Phadoop-2.9" BUILD_FLAG="clean test" TEST_FLAG="test -DskipRat -am" MODULES="-pl submarine-test/e2e" TEST_MODULES="-pl submarine-test/e2e" TEST_PROJECTS="" + addons: + chrome: stable + env: PROFILE="-Phadoop-2.9" BUILD_FLAG="clean package -DskipTests" TEST_FLAG="verify -DskipRat -am" TEST_MODULES="-pl submarine-test/e2e" TEST_PROJECTS="" - name: Test submarine interpreter language: java @@ -190,11 +204,7 @@ matrix: before_script: npm install addons: - apt: - sources: - - google-chrome - packages: - - google-chrome-stable + chrome: stable script: - npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI - npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js @@ -212,8 +222,6 @@ install: - mvn $BUILD_FLAG $MODULES $PROFILE -B before_script: - # https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI - - if [[ -n $TEST_MODULES ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; sleep 3; fi # display info log for debugging - if [[ -n $TEST_MODULES ]]; then echo "MAVEN_OPTS='-Xms1024M -Xmx2048M -XX:MaxPermSize=1024m -XX:-UseGCOverheadLimit -Dorg.slf4j.simpleLogger.defaultLogLevel=info'" > ~/.mavenrc; fi diff --git a/pom.xml b/pom.xml index e33a6c7..b912f46 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,8 @@ <!-- Submarine on Kubernetes --> <k8s.client-java.version>6.0.1</k8s.client-java.version> <jersey.test-framework>2.27</jersey.test-framework> + <!-- integration test--> + <plugin.failsafe.version>2.17</plugin.failsafe.version> </properties> <modules> @@ -140,8 +142,8 @@ <module>submarine-server</module> <module>submarine-all</module> <module>submarine-workbench</module> - <module>submarine-test</module> <module>submarine-dist</module> + <module>submarine-test</module> </modules> <dependencyManagement> diff --git a/submarine-test/e2e/pom.xml b/submarine-test/e2e/pom.xml index 7ad00d5..66ad29e 100644 --- a/submarine-test/e2e/pom.xml +++ b/submarine-test/e2e/pom.xml @@ -16,8 +16,6 @@ <properties> - <maven.compiler.source>${java.version}</maven.compiler.source> - <maven.compiler.target>${java.version}</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> @@ -25,7 +23,6 @@ <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> @@ -78,22 +75,12 @@ <build> - <finalName>submarine-${artifactId}-${project.version}</finalName> <plugins> <plugin> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <source>${java.version}</source> - <target>${java.version}</target> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> - <version>2.17</version> + <version>${plugin.failsafe.version}</version> <executions> <execution> - <id>integration-tests</id> <goals> <goal>integration-test</goal> <goal>verify</goal> @@ -108,23 +95,79 @@ </configuration> </execution> </executions> + <configuration> + <argLine>-Xmx2048m</argLine> + </configuration> </plugin> <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-assembly-plugin</artifactId> - <version>2.4</version> - <configuration> - <archive> - <manifest> - <mainClass>HelloWorldIT</mainClass> - </manifest> - </archive> - <descriptorRefs> - <descriptorRef>jar-with-dependencies</descriptorRef> - </descriptorRefs> + <artifactId>maven-surefire-plugin</artifactId> + <configuration combine.children="append"> + <argLine>-Xmx2g -Xms1g -Dfile.encoding=UTF-8</argLine> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>start-submarine-server</id> + <phase>pre-integration-test</phase> + <configuration> + <target unless="skipTests"> + <exec executable="./submarine-daemon.sh" dir="${submarine.daemon.package.base}" + spawn="false"> + <arg value="start"/> + <arg value="getMysqlJar" /> + </exec> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + <execution> + <id>stop-submarine-server</id> + <phase>post-integration-test</phase> + <configuration> + <target unless="skipTests"> + <exec executable="./submarine-daemon.sh" dir="${submarine.daemon.package.base}" + spawn="false"> + <arg value="stop"/> + </exec> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> + <profiles> + <profile> + <id>using-source-tree</id> + <activation> + <activeByDefault>false</activeByDefault> + </activation> + <properties> + <submarine.daemon.package.base> + ../bin + </submarine.daemon.package.base> + </properties> + </profile> + <profile> + <id>using-packaged-distr</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <properties> + <submarine.daemon.package.base> + ../../submarine-dist/target/submarine-dist-${project.version}-hadoop-2.9/submarine-dist-${project.version}-hadoop-2.9/bin + </submarine.daemon.package.base> + </properties> + </profile> + </profiles> + </project> diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java b/submarine-test/e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java index d65e78d..79d3f5a 100644 --- a/submarine-test/e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/AbstractSubmarineIT.java @@ -17,55 +17,138 @@ package org.apache.submarine; + import com.google.common.base.Function; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.Keys; import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.TakesScreenshot; +import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.Wait; -import java.util.concurrent.TimeUnit; +import org.openqa.selenium.support.ui.WebDriverWait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.concurrent.TimeUnit; + abstract public class AbstractSubmarineIT { - protected static WebDriver driver; - private static Logger LOG = LoggerFactory.getLogger(AbstractSubmarineIT.class); - protected static final long MIN_IMPLICIT_WAIT = 5; - protected static final long MAX_IMPLICIT_WAIT = 30; - protected static final long MAX_BROWSER_TIMEOUT_SEC = 30; - protected static final long MAX_PARAGRAPH_TIMEOUT_SEC = 120; - - protected WebElement pollingWait(final By locator, final long timeWait) { - Wait<WebDriver> wait = new FluentWait<>(driver) - .withTimeout(timeWait, TimeUnit.SECONDS) - .pollingEvery(1, TimeUnit.SECONDS) - .ignoring(NoSuchElementException.class); - - return wait.until(new Function<WebDriver, WebElement>() { - public WebElement apply(WebDriver driver) { - return driver.findElement(locator); - } - }); - } + protected static WebDriver driver; + + protected final static Logger LOG = LoggerFactory.getLogger(AbstractSubmarineIT.class); + protected static final long MIN_IMPLICIT_WAIT = 5; + protected static final long MAX_IMPLICIT_WAIT = 30; + protected static final long MAX_BROWSER_TIMEOUT_SEC = 60; + protected static final long MAX_PARAGRAPH_TIMEOUT_SEC = 120; - protected void clickAndWait(final By locator) { - pollingWait(locator, MAX_IMPLICIT_WAIT).click(); - sleep(1000, false); + protected void setTextOfParagraph(int paragraphNo, String text) { + String editorId = driver.findElement(By.xpath(getParagraphXPath(paragraphNo) + "//div[contains(@class, 'editor')]")).getAttribute("id"); + if (driver instanceof JavascriptExecutor) { + ((JavascriptExecutor) driver).executeScript("ace.edit('" + editorId + "'). setValue('" + text + "')"); + } else { + throw new IllegalStateException("This driver does not support JavaScript!"); } + } + + protected void runParagraph(int paragraphNo) { + By by = By.xpath(getParagraphXPath(paragraphNo) + "//span[@class='icon-control-play']"); + pollingWait(by, 5); + driver.findElement(by).click(); + } + + + protected String getParagraphXPath(int paragraphNo) { + return "(//div[@ng-controller=\"ParagraphCtrl\"])[" + paragraphNo + "]"; + } + + protected String getNoteFormsXPath() { + return "(//div[@id='noteForms'])"; + } + + protected boolean waitForParagraph(final int paragraphNo, final String state) { + By locator = By.xpath(getParagraphXPath(paragraphNo) + + "//div[contains(@class, 'control')]//span[2][contains(.,'" + state + "')]"); + WebElement element = pollingWait(locator, MAX_PARAGRAPH_TIMEOUT_SEC); + return element.isDisplayed(); + } - public static void sleep(long millis, boolean logOutput) { - if (logOutput) { - LOG.info("Starting sleeping for " + (millis / 1000) + " seconds..."); - LOG.info("Caller: " + Thread.currentThread().getStackTrace()[2]); - } - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - LOG.info("Exception in WebDriverManager while getWebDriver "); - } - if (logOutput) { - LOG.info("Finished."); - } + protected String getParagraphStatus(final int paragraphNo) { + By locator = By.xpath(getParagraphXPath(paragraphNo) + + "//div[contains(@class, 'control')]/span[2]"); + + return driver.findElement(locator).getText(); + } + + protected boolean waitForText(final String txt, final By locator) { + try { + WebElement element = pollingWait(locator, MAX_BROWSER_TIMEOUT_SEC); + return txt.equals(element.getText()); + } catch (TimeoutException e) { + return false; } + } + + protected WebElement pollingWait(final By locator, final long timeWait) { + Wait<WebDriver> wait = new FluentWait<>(driver) + .withTimeout(timeWait, TimeUnit.SECONDS) + .pollingEvery(1, TimeUnit.SECONDS) + .ignoring(NoSuchElementException.class); + + return wait.until(new Function<WebDriver, WebElement>() { + public WebElement apply(WebDriver driver) { + return driver.findElement(locator); + } + }); + } + + protected void createNewNote() { + clickAndWait(By.xpath("//div[contains(@class, \"col-md-4\")]/div/h5/a[contains(.,'Create new" + + " note')]")); + + WebDriverWait block = new WebDriverWait(driver, MAX_BROWSER_TIMEOUT_SEC); + block.until(ExpectedConditions.visibilityOfElementLocated(By.id("noteCreateModal"))); + clickAndWait(By.id("createNoteButton")); + block.until(ExpectedConditions.invisibilityOfElementLocated(By.id("createNoteButton"))); + } + + protected void deleteTestNotebook(final WebDriver driver) { + WebDriverWait block = new WebDriverWait(driver, MAX_BROWSER_TIMEOUT_SEC); + driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='moveNoteToTrash(note.id)']")) + .sendKeys(Keys.ENTER); + block.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(".//*[@id='main']//button[@ng-click='moveNoteToTrash(note.id)']"))); + driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'This note will be moved to trash')]" + + "//div[@class='modal-footer']//button[contains(.,'OK')]")).click(); + SubmarineITUtils.sleep(100, false); + } + + protected void deleteTrashNotebook(final WebDriver driver) { + WebDriverWait block = new WebDriverWait(driver, MAX_BROWSER_TIMEOUT_SEC); + driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='removeNote(note.id)']")) + .sendKeys(Keys.ENTER); + block.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(".//*[@id='main']//button[@ng-click='removeNote(note.id)']"))); + driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'This cannot be undone. Are you sure?')]" + + "//div[@class='modal-footer']//button[contains(.,'OK')]")).click(); + SubmarineITUtils.sleep(100, false); + } + + protected void clickAndWait(final By locator) { + pollingWait(locator, MAX_IMPLICIT_WAIT).click(); + SubmarineITUtils.sleep(1000, false); + } + + protected void handleException(String message, Exception e) throws Exception { + LOG.error(message, e); + File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); + LOG.error("ScreenShot::\ndata:image/png;base64," + new String(Base64.encodeBase64(FileUtils.readFileToByteArray(scrFile)))); + throw e; + } + } diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/ChromeWebDriverProvider.java b/submarine-test/e2e/src/test/java/org/apache/submarine/ChromeWebDriverProvider.java new file mode 100644 index 0000000..ccbc106 --- /dev/null +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/ChromeWebDriverProvider.java @@ -0,0 +1,148 @@ +/* + * 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.submarine; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.SystemUtils; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.rauschig.jarchivelib.Archiver; +import org.rauschig.jarchivelib.ArchiverFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class ChromeWebDriverProvider implements WebDriverProvider { + + protected final static Logger LOG = LoggerFactory.getLogger(AbstractSubmarineIT.class); + public final static String chromeDriverName = "chromedriver"; + + @Override + public String getWebDriverVersion() { + String chromeVersion = getChromeVersion(); + String chromeDriverVersion = getChromeDriverVersion(chromeVersion); + return chromeDriverVersion; + } + + @Override + public String downloadWebDriver(String webDriverVersion) { + String downLoadsDir = FileUtils.getTempDirectory().toString(); + String tempPath = downLoadsDir + "/chrome/"; + String chromeDriverUrlString = + "https://chromedriver.storage.googleapis.com/" + webDriverVersion + "/"; + + LOG.info("Chrome driver version: " + webDriverVersion + + ", will be downloaded to " + tempPath); + String chromeDriverZipName = ""; + try { + if (SystemUtils.IS_OS_WINDOWS) { + if (System.getProperty("sun.arch.data.model").equals("32")) { + chromeDriverZipName = "chromedriver_win32.zip"; + } + } else if (SystemUtils.IS_OS_LINUX) { + if (System.getProperty("sun.arch.data.model").equals("64")) { + chromeDriverZipName = "chromedriver_linux64.zip"; + } + } else if (SystemUtils.IS_OS_MAC_OSX) { + chromeDriverZipName = "chromedriver_mac64.zip"; + } + chromeDriverUrlString += chromeDriverZipName; + + File chromeDriver = new File(tempPath + chromeDriverName); + File chromeDriverZip = new File(tempPath + chromeDriverZipName); + File chromeDriverDir = new File(tempPath); + URL driverUrl = new URL(chromeDriverUrlString); + if (!chromeDriver.exists()) { + FileUtils.copyURLToFile(driverUrl, chromeDriverZip); + if (SystemUtils.IS_OS_WINDOWS) { + Archiver archiver = ArchiverFactory.createArchiver("zip"); + archiver.extract(chromeDriverZip, chromeDriverDir); + } else { + Archiver archiver = ArchiverFactory.createArchiver("zip"); + archiver.extract(chromeDriverZip, chromeDriverDir); + LOG.info("Get chromeDriver:" + chromeDriver.getAbsolutePath()); + } + } + + } catch (IOException e) { + LOG.error("Download of chromeDriver version: " + webDriverVersion + ", falied in path " + tempPath); + } + LOG.info("Download the chromeDriver to " + tempPath +" successfully."); + return tempPath + chromeDriverName; + } + + @Override + public WebDriver createWebDriver(String webDriverPath) { + System.setProperty("webdriver.chrome.driver", webDriverPath); + ChromeOptions chromeOptions = new ChromeOptions(); + chromeOptions.addArguments("--headless"); + return new ChromeDriver(chromeOptions); + } + + public String getChromeVersion() { + try { + String versionCmd = "google-chrome --version"; + if (System.getProperty("os.name").startsWith("Mac OS")) { + versionCmd = "/Applications/Google Chrome.app/Contents/MacOS/google chrome --version"; + } + String versionString = (String) CommandExecutor + .executeCommandLocalHost(versionCmd, false, ProcessData.Types_Of_Data.OUTPUT); + + LOG.info("The version of chrome is " + versionString); + return versionString.replaceAll("Google Chrome", "").trim(); + } catch (Exception e) { + LOG.error("Exception in WebDriverManager while getWebDriver ", e); + return ""; + } + } + + public String getChromeDriverVersion(String chromeVersion) { + // chromeVersion is like 75.0.3770.140. + // Get the major version of chrome, like 75. + String chromeMajorVersion = + chromeVersion.substring(0, chromeVersion.indexOf(".")); + String chromeDriverIndexUrl = + "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + + chromeMajorVersion; + + // Get the chrome driver version according to the chrome. + File chromeDriverVersionFile = new File( + FileUtils.getTempDirectory().toString() + "/chromeDriverVersion"); + + String chromeDriverVersion = ""; + try { + FileUtils.copyURLToFile( + new URL(chromeDriverIndexUrl), chromeDriverVersionFile); + chromeDriverVersion = new String( + Files.readAllBytes(chromeDriverVersionFile.toPath()), + StandardCharsets.UTF_8); + chromeDriverVersionFile.delete(); + } catch (Exception e) { + LOG.error("Exception in getting chromeDriverVersion", e); + } + + LOG.info("The required chrome driver version is " + chromeDriverVersion); + return chromeDriverVersion; + } +} diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/CommandExecutor.java b/submarine-test/e2e/src/test/java/org/apache/submarine/CommandExecutor.java index 81e083f..65b74ad 100644 --- a/submarine-test/e2e/src/test/java/org/apache/submarine/CommandExecutor.java +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/CommandExecutor.java @@ -1,42 +1,74 @@ /* - * 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 + * 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 + * 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. + * 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.submarine; -import java.io.BufferedReader; -import java.io.InputStreamReader; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class CommandExecutor { - private Process p; - private Logger LOG = LoggerFactory.getLogger(CommandExecutor.class); - public Object executeCommandLocalHost(String command) { - String outputString = ""; - try { - p = Runtime.getRuntime().exec(command); - BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); - outputString = in.readLine(); - in.close(); - } catch (Exception e) { - LOG.info("Exception in executeCommandLocalHost while execute : " + command); - } - return outputString; + + public final static Logger LOG = LoggerFactory.getLogger(CommandExecutor.class); + + public enum IGNORE_ERRORS { + TRUE, + FALSE + } + + public static int NORMAL_EXIT = 0; + + private static IGNORE_ERRORS DEFAULT_BEHAVIOUR_ON_ERRORS = IGNORE_ERRORS.TRUE; + + public static Object executeCommandLocalHost(String[] command, boolean printToConsole, ProcessData.Types_Of_Data type, IGNORE_ERRORS ignore_errors) { + List<String> subCommandsAsList = new ArrayList<>(Arrays.asList(command)); + String mergedCommand = StringUtils.join(subCommandsAsList, " "); + + LOG.info("Sending command \"" + mergedCommand + "\" to localhost"); + + ProcessBuilder processBuilder = new ProcessBuilder(command); + Process process = null; + try { + process = processBuilder.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ProcessData data_of_process = new ProcessData(process, printToConsole); + Object output_of_process = data_of_process.getData(type); + int exit_code = data_of_process.getExitCodeValue(); + + if (!printToConsole) + LOG.trace(output_of_process.toString()); + else + LOG.debug(output_of_process.toString()); + if (ignore_errors == IGNORE_ERRORS.FALSE && exit_code != NORMAL_EXIT) { + LOG.error(String.format("*********************Command '%s' failed with exitcode %s *********************", mergedCommand, exit_code)); } -} + return output_of_process; + } + + public static Object executeCommandLocalHost(String command, boolean printToConsole, ProcessData.Types_Of_Data type) { + return executeCommandLocalHost(new String[]{"bash", "-c", command}, printToConsole, type, DEFAULT_BEHAVIOUR_ON_ERRORS); + } + +} diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/FirefoxWebDriverProvider.java b/submarine-test/e2e/src/test/java/org/apache/submarine/FirefoxWebDriverProvider.java new file mode 100644 index 0000000..5fc01f9 --- /dev/null +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/FirefoxWebDriverProvider.java @@ -0,0 +1,152 @@ +/* + * 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.submarine; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.SystemUtils; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.firefox.FirefoxBinary; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxDriverLogLevel; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.firefox.FirefoxProfile; +import org.openqa.selenium.firefox.GeckoDriverService; +import org.rauschig.jarchivelib.Archiver; +import org.rauschig.jarchivelib.ArchiverFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +public class FirefoxWebDriverProvider implements WebDriverProvider { + + protected final static Logger LOG = LoggerFactory.getLogger(AbstractSubmarineIT.class); + private static String GECKODRIVER_VERSION = "0.25.0"; + public final static String geckoDriverName = "geckodriver"; + + @Override + public String getWebDriverVersion() { + return GECKODRIVER_VERSION; + } + + @Override + public String downloadWebDriver(String webDriverVersion) { + String downLoadsDir = FileUtils.getTempDirectory().toString(); + String tempPath = downLoadsDir + "/firefox/"; + String geekoDriverUrlString = + "https://github.com/mozilla/geckodriver/releases/download/v" + webDriverVersion + + "/geckodriver-v" + webDriverVersion + "-"; + + LOG.info("Geeko version: " + webDriverVersion + ", will be downloaded to " + tempPath); + try { + if (SystemUtils.IS_OS_WINDOWS) { + if (System.getProperty("sun.arch.data.model").equals("64")) { + geekoDriverUrlString += "win64.zip"; + } else { + geekoDriverUrlString += "win32.zip"; + } + } else if (SystemUtils.IS_OS_LINUX) { + if (System.getProperty("sun.arch.data.model").equals("64")) { + geekoDriverUrlString += "linux64.tar.gz"; + } else { + geekoDriverUrlString += "linux32.tar.gz"; + } + } else if (SystemUtils.IS_OS_MAC_OSX) { + geekoDriverUrlString += "macos.tar.gz"; + } + + File geekoDriver = new File(tempPath + "geckodriver"); + File geekoDriverZip = new File(tempPath + "geckodriver.tar"); + File geekoDriverDir = new File(tempPath); + URL geekoDriverUrl = new URL(geekoDriverUrlString); + if (!geekoDriver.exists()) { + FileUtils.copyURLToFile(geekoDriverUrl, geekoDriverZip); + if (SystemUtils.IS_OS_WINDOWS) { + Archiver archiver = ArchiverFactory.createArchiver("zip"); + archiver.extract(geekoDriverZip, geekoDriverDir); + } else { + Archiver archiver = ArchiverFactory.createArchiver("tar", "gz"); + archiver.extract(geekoDriverZip, geekoDriverDir); + LOG.info("Get geckoDriver:" + geekoDriver.getAbsolutePath()); + } + } + + } catch (IOException e) { + LOG.error("Download of Gecko version: " + webDriverVersion + + ", falied in path " + tempPath); + } + LOG.info("Download the firefox Gecko driver to " + tempPath + + " successfully."); + return tempPath + geckoDriverName; + } + + @Override + public WebDriver createWebDriver(String webDriverPath) { + FirefoxBinary ffox = new FirefoxBinary(); + if ("true".equals(System.getenv("TRAVIS"))) { + // xvfb is supposed to run with DISPLAY 99 + ffox.setEnvironmentProperty("DISPLAY", ":99"); + } + ffox.addCommandLineOptions("--headless"); + + FirefoxProfile profile = new FirefoxProfile(); + profile.setPreference("browser.download.folderList", 2); + profile.setPreference("browser.download.dir", + FileUtils.getTempDirectory().toString() + "/firefox/"); + profile.setPreference("browser.helperApps.alwaysAsk.force", false); + profile.setPreference("browser.download.manager.showWhenStarting", false); + profile.setPreference("browser.download.manager.showAlertOnComplete", false); + profile.setPreference("browser.download.manager.closeWhenDone", true); + profile.setPreference("app.update.auto", false); + profile.setPreference("app.update.enabled", false); + profile.setPreference("dom.max_script_run_time", 0); + profile.setPreference("dom.max_chrome_script_run_time", 0); + profile.setPreference("browser.helperApps.neverAsk.saveToDisk", + "application/x-ustar,application/octet-stream,application/zip,text/csv,text/plain"); + profile.setPreference("network.proxy.type", 0); + + System.setProperty( + GeckoDriverService.GECKO_DRIVER_EXE_PROPERTY, webDriverPath); + System.setProperty( + FirefoxDriver.SystemProperty.DRIVER_USE_MARIONETTE, "false"); + + FirefoxOptions firefoxOptions = new FirefoxOptions(); + firefoxOptions.setBinary(ffox); + firefoxOptions.setProfile(profile); + firefoxOptions.setLogLevel(FirefoxDriverLogLevel.TRACE); + return new FirefoxDriver(firefoxOptions); + } + + public static int getFirefoxVersion() { + try { + String firefoxVersionCmd = "firefox -v"; + if (System.getProperty("os.name").startsWith("Mac OS")) { + firefoxVersionCmd = "/Applications/Firefox.app/Contents/MacOS/" + firefoxVersionCmd; + } + String versionString = (String) CommandExecutor + .executeCommandLocalHost(firefoxVersionCmd, false, ProcessData.Types_Of_Data.OUTPUT); + return Integer + .valueOf(versionString.replaceAll("Mozilla Firefox", "").trim().substring(0, 2)); + } catch (Exception e) { + LOG.error("Exception in WebDriverManager while getWebDriver ", e); + return -1; + } + } +} diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/ProcessData.java b/submarine-test/e2e/src/test/java/org/apache/submarine/ProcessData.java new file mode 100644 index 0000000..de201f9 --- /dev/null +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/ProcessData.java @@ -0,0 +1,257 @@ +/* + * 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.submarine; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProcessData { + public enum Types_Of_Data { + OUTPUT, + ERROR, + EXIT_CODE, + STREAMS_MERGED, + PROCESS_DATA_OBJECT + } + + public final static Logger LOG = LoggerFactory.getLogger(ProcessData.class); + + private Process checked_process; + private boolean printToConsole = false; + private boolean removeRedundantOutput = true; + + public ProcessData(Process connected_process, boolean printToConsole, int silenceTimeout, TimeUnit timeUnit) { + this.checked_process = connected_process; + this.printToConsole = printToConsole; + this.silenceTimeout = TimeUnit.MILLISECONDS.convert(silenceTimeout, timeUnit); + } + + public ProcessData(Process connected_process, boolean printToConsole, int silenceTimeoutSec) { + this.checked_process = connected_process; + this.printToConsole = printToConsole; + this.silenceTimeout = TimeUnit.MILLISECONDS.convert(silenceTimeoutSec, TimeUnit.SECONDS); + } + + public ProcessData(Process connected_process, boolean printToConsole) { + this.checked_process = connected_process; + this.printToConsole = printToConsole; + } + + public ProcessData(Process connected_process) { + this.checked_process = connected_process; + this.printToConsole = true; + } + + + boolean returnCodeRetrieved = false; + + private String outPutStream = null; + private String errorStream = null; + private int returnCode; + private long silenceTimeout = 10 * 60 * 1000; + private final long unconditionalExitDelayMinutes = 30; + + public static boolean isRunning(Process process) { + try { + process.exitValue(); + return false; + } catch (IllegalThreadStateException e) { + return true; + } + } + + public Object getData(Types_Of_Data type) { + //TODO get rid of Pseudo-terminal will not be allocated because stdin is not a terminal. + switch (type) { + case OUTPUT: { + return this.getOutPutStream(); + } + case ERROR: { + return this.getErrorStream(); + } + case EXIT_CODE: { + return this.getExitCodeValue(); + } + case STREAMS_MERGED: { + return this.getOutPutStream() + "\n" + this.getErrorStream(); + } + case PROCESS_DATA_OBJECT: { + this.getErrorStream(); + return this; + } + default: { + throw new IllegalArgumentException("Data Type " + type + " not supported yet!"); + } + } + } + + public int getExitCodeValue() { + try { + if (!returnCodeRetrieved) { + this.checked_process.waitFor(); + this.returnCode = this.checked_process.exitValue(); + this.returnCodeRetrieved = true; + this.checked_process.destroy(); + } + } catch (Exception inter) { + throw new RuntimeException("Couldn't finish waiting for process " + this.checked_process + " termination", inter); + } + return this.returnCode; + } + + public String getOutPutStream() { + if (this.outPutStream == null) { + try { + buildOutputAndErrorStreamData(); + } catch (Exception e) { + throw new RuntimeException("Couldn't retrieve Output Stream data from process: " + this.checked_process.toString(), e); + + } + } + this.outPutStream = this.outPutStream.replace("Pseudo-terminal will not be allocated because stdin is not a terminal.", ""); + this.errorStream = this.errorStream.replace("Pseudo-terminal will not be allocated because stdin is not a terminal.", ""); + return this.outPutStream; + } + + public String getErrorStream() { + if (this.errorStream == null) { + try { + buildOutputAndErrorStreamData(); + } catch (Exception e) { + throw new RuntimeException("Couldn't retrieve Error Stream data from process: " + this.checked_process.toString(), e); + + } + } + this.outPutStream = this.outPutStream.replace("Pseudo-terminal will not be allocated because stdin is not a terminal.", ""); + this.errorStream = this.errorStream.replace("Pseudo-terminal will not be allocated because stdin is not a terminal.", ""); + return this.errorStream; + } + + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(String.format("[OUTPUT STREAM]\n%s\n", this.outPutStream)); + result.append(String.format("[ERROR STREAM]\n%s\n", this.errorStream)); + result.append(String.format("[EXIT CODE]\n%d", this.returnCode)); + return result.toString(); + } + + private void buildOutputAndErrorStreamData() throws IOException { + StringBuilder sbInStream = new StringBuilder(); + StringBuilder sbErrorStream = new StringBuilder(); + + try { + InputStream in = this.checked_process.getInputStream(); + InputStream inErrors = this.checked_process.getErrorStream(); + BufferedReader inReader = new BufferedReader(new InputStreamReader(in)); + BufferedReader inReaderErrors = new BufferedReader(new InputStreamReader(inErrors)); + LOG.trace("Started retrieving data from streams of attached process: " + this.checked_process); + + long lastStreamDataTime = System.currentTimeMillis(); //Store start time to be able to finish method if command hangs + long unconditionalExitTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(unconditionalExitDelayMinutes, TimeUnit.MINUTES); // Stop after 'unconditionalExitDelayMinutes' even if process is alive and sending output + final int BUFFER_LEN = 300; + char charBuffer[] = new char[BUFFER_LEN]; //Use char buffer to read output, size can be tuned. + boolean outputProduced = true; //Flag to check if previous iteration produced any output + while (isRunning(this.checked_process) || outputProduced) { //Continue if process is alive or some output was produced on previous iteration and there may be still some data to read. + outputProduced = false; + SubmarineITUtils.sleep(100, false); //Some local commands can exit fast, but immediate stream reading will give no output and after iteration, 'while' condition will be false so we will not read out any output while it is still there, just need to wait for some time for it to appear in streams. + + StringBuilder tempSB = new StringBuilder(); + while (inReader.ready()) { + tempSB.setLength(0); // clean temporary StringBuilder + int readCount = inReader.read(charBuffer, 0, BUFFER_LEN); //read up to 'BUFFER_LEN' chars to buffer + if (readCount < 1) { // if nothing read or error occurred + break; + } + tempSB.append(charBuffer, 0, readCount); + + sbInStream.append(tempSB); + if (tempSB.length() > 0) { + outputProduced = true; //set flag to know that we read something and there may be moire data, even if process already exited + } + + lastStreamDataTime = System.currentTimeMillis(); //remember last time data was read from streams to be sure we are not looping infinitely + } + + tempSB = new StringBuilder(); //Same, but for error stream + while (inReaderErrors.ready()) { + tempSB.setLength(0); + int readCount = inReaderErrors.read(charBuffer, 0, BUFFER_LEN); + if (readCount < 1) { + break; + } + tempSB.append(charBuffer, 0, readCount); + sbErrorStream.append(tempSB); + if (tempSB.length() > 0) { + outputProduced = true; + String temp = new String(tempSB); + temp = temp.replaceAll("Pseudo-terminal will not be allocated because stdin is not a terminal.", ""); + //TODO : error stream output need to be improved, because it outputs downloading information. + if (printToConsole) { + if (!temp.trim().equals("")) { + if (temp.toLowerCase().contains("error") || temp.toLowerCase().contains("failed")) { + LOG.warn(temp.trim()); + } else { + LOG.debug(temp.trim()); + } + } + } + } + lastStreamDataTime = System.currentTimeMillis(); + } + + + if ((System.currentTimeMillis() - lastStreamDataTime > silenceTimeout) || //Exit if silenceTimeout ms has passed from last stream read. Means process is alive but not sending any data. + (System.currentTimeMillis() > unconditionalExitTime)) { //Exit unconditionally - guards against alive process continuously sending data. + LOG.info("Conditions: " + (System.currentTimeMillis() - lastStreamDataTime > silenceTimeout) + " " + + (System.currentTimeMillis() > unconditionalExitTime)); + this.checked_process.destroy(); + try { + if ((System.currentTimeMillis() > unconditionalExitTime)) { + LOG.error( + "!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Unconditional exit occured@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@!\nsome process hag up for more than " + + unconditionalExitDelayMinutes + " minutes."); + } + LOG.error("!##################################!"); + StringWriter sw = new StringWriter(); + Exception e = new Exception("Exited from buildOutputAndErrorStreamData by timeout"); + e.printStackTrace(new PrintWriter(sw)); //Get stack trace + LOG.error(String.valueOf(e), e); + } catch (Exception ignore) { + LOG.info("Exception in ProcessData while buildOutputAndErrorStreamData ", ignore); + } + break; + } + } + + in.close(); + inErrors.close(); + } finally { + this.outPutStream = sbInStream.toString(); + this.errorStream = sbErrorStream.toString(); + } + } +} diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/SubmarineITUtils.java b/submarine-test/e2e/src/test/java/org/apache/submarine/SubmarineITUtils.java new file mode 100644 index 0000000..082aa9c --- /dev/null +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/SubmarineITUtils.java @@ -0,0 +1,60 @@ +/* + * 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.submarine; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.openqa.selenium.WebDriver; +import java.util.concurrent.TimeUnit; + +public class SubmarineITUtils { + + public final static Logger LOG = LoggerFactory.getLogger(SubmarineITUtils.class); + + public static void sleep(long millis, boolean logOutput) { + if (logOutput) { + LOG.info("Starting sleeping for " + (millis / 1000) + " seconds..."); + LOG.info("Caller: " + Thread.currentThread().getStackTrace()[2]); + } + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + LOG.error("Exception in WebDriverManager while getWebDriver ", e); + } + if (logOutput) { + LOG.info("Finished."); + } + } + + public static void restartSubmarineServer() { + CommandExecutor.executeCommandLocalHost("../bin/submarine-daemon.sh restart", + false, ProcessData.Types_Of_Data.OUTPUT); + //wait for server to start. + sleep(5000, false); + } + + public static void turnOffImplicitWaits(WebDriver driver) { + driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS); + } + + public static void turnOnImplicitWaits(WebDriver driver) { + driver.manage().timeouts().implicitlyWait(AbstractSubmarineIT.MAX_IMPLICIT_WAIT, + TimeUnit.SECONDS); + } +} diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverManager.java b/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverManager.java index 1c9e474..d6abfc9 100644 --- a/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverManager.java +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverManager.java @@ -17,109 +17,99 @@ package org.apache.submarine; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.SystemUtils; +import static org.junit.Assert.fail; + +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.firefox.FirefoxDriver; -import org.rauschig.jarchivelib.Archiver; -import org.rauschig.jarchivelib.ArchiverFactory; -import java.io.File; -import java.net.URL; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.WebDriverWait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + public class WebDriverManager { - private static Logger LOG = LoggerFactory.getLogger(WebDriverManager.class); - private static String GECKODRIVER_VERSION = "0.25.0"; - private static String downLoadsDir = ""; - private static CommandExecutor cmdExec = new CommandExecutor(); - - public static WebDriver getWebDriver() { - WebDriver driver = null; - // firefox webdriver - if(driver == null) { - try { - // download GeckoDriver - downLoadsDir = System.getProperty("user.dir"); - String tempPath = downLoadsDir + "/Driver/"; - downloadGeckoDriver(tempPath); - if(SystemUtils.IS_OS_MAC_OSX) { - String command = "chmod +x " + tempPath + "geckodriver"; - cmdExec.executeCommandLocalHost(command); - } - System.setProperty("webdriver.gecko.driver", tempPath + "geckodriver"); - // TODO(Kai-Hsun Chen): set firefox preference (refer to WebDriverManager.java:74 ~ 94 in Zeppelin) - // Initialize firefox WebDriver - String firefoxVersion = getFirefoxVersion(); - LOG.info("Firefox version " + firefoxVersion + " detected"); - driver = new FirefoxDriver(); - } catch (Exception e) { - LOG.info("Exception in WebDriverManager while FireFox Driver"); - } - } - - String url = "http://127.0.0.1:32777"; - driver.get(url); - return driver; + + public final static Logger LOG = LoggerFactory.getLogger(WebDriverManager.class); + + private static String downLoadsDir = ""; + + private static boolean webDriverIsDownloaded = false; + + private static String webDriverPath = ""; + + public static WebDriver getWebDriver() { + WebDriver driver = null; + + if (driver == null) { + try { + WebDriverProvider provide = new ChromeWebDriverProvider(); + driver = generateWebDriver(provide); + } catch (Exception e) { + LOG.error("Exception in WebDriverManager while ChromeDriver ", e); + } } - - // TODO(Kai-Hsun Chen): need to set the path of geckodriver - public static void downloadGeckoDriver(String tempPath) { - String geckoDriverUrlString = "https://github.com/mozilla/geckodriver/releases/download/v" + GECKODRIVER_VERSION + "/geckodriver-v" + GECKODRIVER_VERSION + "-"; - LOG.info("Gecko version: v" + GECKODRIVER_VERSION + ", will be downloaded to " + tempPath); - try { - if (SystemUtils.IS_OS_WINDOWS) { - if (System.getProperty("sun.arch.data.model").equals("64")) { - geckoDriverUrlString += "win64.zip"; - } else { - geckoDriverUrlString += "win32.zip"; - } - } else if (SystemUtils.IS_OS_LINUX) { - if (System.getProperty("sun.arch.data.model").equals("64")) { - geckoDriverUrlString += "linux64.tar.gz"; - } else { - geckoDriverUrlString += "linux32.tar.gz"; - } - } else if (SystemUtils.IS_OS_MAC_OSX) { - geckoDriverUrlString += "macos.tar.gz"; - } - - File geckoDriver = new File(tempPath + "geckodriver"); - File geckoDriverZip = new File(tempPath + "geckodriver.tar"); - File geckoDriverDir = new File(tempPath); - URL geckoDriverUrl = new URL(geckoDriverUrlString); - if (!geckoDriver.exists()) { - FileUtils.copyURLToFile(geckoDriverUrl, geckoDriverZip); - if (SystemUtils.IS_OS_WINDOWS) { - Archiver archiver = ArchiverFactory.createArchiver("zip"); - archiver.extract(geckoDriverZip, geckoDriverDir); - } else { - Archiver archiver = ArchiverFactory.createArchiver("tar", "gz"); - archiver.extract(geckoDriverZip, geckoDriverDir); - } - } else { - LOG.info("Gecko version: v" + GECKODRIVER_VERSION + " has already existed in path " + tempPath); - return; - } - } catch (Exception e) { - LOG.info("[FAIL] Download of Gecko version: v" + GECKODRIVER_VERSION + ", falied in path " + tempPath); - return; - } - LOG.info("[SUCCESS] Download of Gecko version: " + GECKODRIVER_VERSION); + + if (driver == null) { + try { + WebDriverProvider provide = new FirefoxWebDriverProvider(); + driver = generateWebDriver(provide); + } catch (Exception e) { + LOG.error("Exception in WebDriverManager while FireFox Driver ", e); + } } - // TODO(Kai-Hsun Chen): need to be tested on MacOS, and Linux - public static String getFirefoxVersion() { - String firefoxVersionCmd = "firefox -v"; - String version = ""; - if (System.getProperty("os.name").startsWith("Mac OS")) { - firefoxVersionCmd = "/Applications/Firefox.app/Contents/MacOS/" + firefoxVersionCmd; - } - try { - version = cmdExec.executeCommandLocalHost(firefoxVersionCmd).toString(); - } catch (Exception e) { - LOG.info("Exception in WebDriverManager while getFirefoxVersion"); - } - return version; + String url; + if (System.getenv("url") != null) { + url = System.getenv("url"); + } else { + url = "http://localhost:8080"; + } + + long start = System.currentTimeMillis(); + boolean loaded = false; + driver.manage().timeouts().implicitlyWait(AbstractSubmarineIT.MAX_IMPLICIT_WAIT, + TimeUnit.SECONDS); + driver.get(url); + + while (System.currentTimeMillis() - start < 60 * 1000) { + // wait for page load + try { + (new WebDriverWait(driver, 60)).until(new ExpectedCondition<Boolean>() { + @Override + public Boolean apply(WebDriver d) { + // return d.findElement(By.tagName("div")) + return d.findElement(By.tagName("submarine-root")) + .isDisplayed(); + } + }); + loaded = true; + break; + } catch (TimeoutException e) { + LOG.info("Exception in WebDriverManager while WebDriverWait ", e); + driver.navigate().to(url); + } + } + + if (loaded == false) { + fail(); + } + + driver.manage().window().maximize(); + return driver; + } + + private static WebDriver generateWebDriver(WebDriverProvider provide) { + if (!webDriverIsDownloaded) { + String webDriverVersion = provide.getWebDriverVersion(); + webDriverPath = provide.downloadWebDriver(webDriverVersion); + if (StringUtils.isNotBlank(webDriverPath)) { + webDriverIsDownloaded = true; + } } + WebDriver driver = provide.createWebDriver(webDriverPath); + return driver; + } } diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverProvider.java b/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverProvider.java new file mode 100644 index 0000000..c6d645d --- /dev/null +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/WebDriverProvider.java @@ -0,0 +1,42 @@ +/* + * 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.submarine; + +import org.openqa.selenium.WebDriver; + +public interface WebDriverProvider { + /** + * Get webdriver version according to web browser. + * @return the required version of web driver + */ + public String getWebDriverVersion(); + + /** + * Download the web driver. + * @param webDriverVersion + * @return + */ + public String downloadWebDriver(String webDriverVersion); + + /** + * create a new web driver + * @param webDriverPath + * @return a new web driver + */ + public WebDriver createWebDriver(String webDriverPath); +} diff --git a/submarine-test/e2e/src/test/java/org/apache/submarine/integration/loginIT.java b/submarine-test/e2e/src/test/java/org/apache/submarine/integration/loginIT.java index 6f09cd9..ec4a602 100644 --- a/submarine-test/e2e/src/test/java/org/apache/submarine/integration/loginIT.java +++ b/submarine-test/e2e/src/test/java/org/apache/submarine/integration/loginIT.java @@ -20,9 +20,16 @@ package org.apache.submarine.integration; import org.apache.submarine.AbstractSubmarineIT; import org.apache.submarine.WebDriverManager; import org.openqa.selenium.By; -import org.testng.annotations.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class loginIT extends AbstractSubmarineIT { + + public final static Logger LOG = LoggerFactory.getLogger(loginIT.class); -public class loginIT extends AbstractSubmarineIT{ @BeforeClass public static void startUp(){ driver = WebDriverManager.getWebDriver(); @@ -35,9 +42,12 @@ public class loginIT extends AbstractSubmarineIT{ @Test public void loginUser() throws Exception { + LOG.info("Start to login user to submarine workbench."); pollingWait(By.cssSelector("input[ng-reflect-name='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin"); pollingWait(By.cssSelector("input[ng-reflect-name='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys("admin"); - pollingWait(By.cssSelector("button[class='login-form-button ant-btn ant-btn-primary']"), MAX_BROWSER_TIMEOUT_SEC).click(); - sleep(1000,false); + clickAndWait(By.cssSelector("button[class='login-form-button ant-btn ant-btn-primary']")); + // Validate login result. + pollingWait(By.cssSelector("a[routerlink='/workbench/dashboard']"), MAX_BROWSER_TIMEOUT_SEC); + LOG.info("User login is done."); } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@submarine.apache.org For additional commands, e-mail: dev-h...@submarine.apache.org