Programatic SauceLabs隧道控制

时间:2015-11-23 20:01:03

标签: selenium-webdriver saucelabs

有没有人有过以编程方式为Selenium WebDriver测试控制SauceLabs SauceConnect隧道的经验?特别是从Java代码中。 SauceLabs文档中的示例假定在执行测试之前手动创建隧道,或者所有测试都使用某个永久隧道。

对于每个测试,隧道都需要是唯一的,并且支持在同一个盒子上同时运行多个隧道/测试。使用该项目的任何人都应该能够使用隧道执行测试,无需手动步骤来创建隧道或执行隧道软件的特殊安装和配置。

2 个答案:

答案 0 :(得分:2)

我做了一些研究,并提出了一种从代码中控制我的SauceConnect隧道的方法。以下是如何让其工作的简短摘要,以防其他人希望这样做。我使用此设置在同一台服务器上运行多个隧道,并将每个隧道绑定到站点代理服务器上。这些示例假定您在JVM上运行代码。

我在博文Controlling Sauce Connect中汇总了更详细的文章。

<强>依赖

将以下依赖项添加到项目中。 com.saucelabs.ci-sauce

然后你可以使用下面的代码调用库来启动和停止隧道。

启动隧道

Process tunnel = sauceTunnelManager.openConnection( 
        sauceUser,      // username 
        sauceKey,       // apiKey 
        port,           // port 
        null,           // sauceConnectJar 
        tunnelOptions,  // Tunnel options 
        null,           // printStream 
        null,           // verboseLogging 
        null            // sauceConnectPath );

关闭隧道

sauceTunnelManager.closeTunnelsForPlan(
     sauceUser,      // username (same as start tunnel)
     tunnelOptions,  // tunnelOptions (same as start tunnel)
     null);

答案 1 :(得分:0)

下面是我编写的一系列java类,它们以编程方式控制SauceTunnel,作为更广泛的解决方案的一部分,使用户能够使用JUnit与BrowserStack或SauceLabs交谈:

Repository有一个包含Sauce-Connect.jar的lib目录。

<强> SauceLabsTunnel

package au.com.somecompany.devops.tunnels;

import au.com.somecompany.devops.tunnels.channel.Channel;
import au.com.somecompany.devops.tunnels.channel.SauceLabsChannel;
import au.com.somecompany.devops.utils.Drop;

public class SauceLabsTunnel extends Thread implements Runnable {
    private Channel Jar;
    private Drop Tunnelled;
    private Drop Tested;

    public SauceLabsTunnel(Drop tunnelled, Drop tested){
        String command = "java -jar lib/Sauce-Connect.jar ";
        String successMessage = "Successful handshake with Sauce Connect server";
        String failureMessage = "Could not reach Sauce Labs REST API";
        String outputTerminal = "Connected! You may start your tests.";

        Jar = new SauceLabsChannel(command, System.getProperty("ACCESSKEY"), successMessage, failureMessage, outputTerminal, System.getProperty("USERNAME"));
        Tunnelled = tunnelled;
        Tested = tested;
    }

    @Override
    public void run() {
        Jar.Launch();

        while(!Jar.IsJARSuccessfullyLaunched()){
            Jar.Launch();
        }
        Jar.PrintOutput();
        Tunnelled.put("TUNNELLED");

        for (String message = Tested.take(); !message.equals("TESTED"); message = Tested.take()) {
            System.out.println("I see Testing is completed. Closing Tunnel...");
        }
        Shutdown();
    }

    public void Shutdown(){
        Jar.Shutdown();
    }
}

<强>频道

package au.com.somecompany.devops.tunnels.channel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Channel {
    protected Process Process;
    protected String ChannelCommand = null;
    protected String ProcessOutput = null;
    protected String SuccessMessage = null;
    protected String FailureMessage = null;
    protected String OutputTerminal = null;

    public Channel(String command, String successMessage, String failureMessage, String outputTerminal){
        ChannelCommand = command;
        SuccessMessage = successMessage;
        FailureMessage = failureMessage;
        OutputTerminal = outputTerminal;
    }

    public void Launch(){
        Process = ExecuteJAR();
        ProcessOutput = GetOutput(Process.getInputStream());
    }

    public boolean IsJARSuccessfullyLaunched(){
        return ProcessOutput.contains(SuccessMessage) && 
               !ProcessOutput.contains(FailureMessage);
    }

    public void PrintOutput(){
        System.out.print(ProcessOutput);
    }

    public void Shutdown(){
        Process.destroy();
    }

    private String GetOutput(InputStream is){
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        StringBuffer sb = new StringBuffer();
        try {
            String s = br.readLine();
            if(s == null) {
                return null;
            }
            while(!s.contains(OutputTerminal)){
                sb.append(s).append("\n");
                s = br.readLine();
                if(s == null || s.contains(OutputTerminal)){
                    break;
                }
            }
        } catch (IOException e){
            e.printStackTrace();
        }
        return sb.toString();
    }

    private Process ExecuteJAR() {
        Process p;
        try {
            p = Runtime.getRuntime().exec(ChannelCommand);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return p;
    }
}

<强> SauceLabsChannel

package au.com.somecompany.devops.tunnels.channel;

public class SauceLabsChannel extends Channel {
    public SauceLabsChannel(String command, String accessKey, String successMessage, String failureMessage, String outputTerminal, String username){
        super(command, successMessage, failureMessage, outputTerminal);
        ChannelCommand += username + " " + accessKey;
    }
}

下拉

package au.com.somecompany.devops.utils;

public class Drop {
    // Message sent from producer
    // to consumer.
    private String message;
    // True if consumer should wait
    // for producer to send message,
    // false if producer should wait for
    // consumer to retrieve message.
    private boolean empty = true;

    public synchronized String take() {
        // Wait until message is
        // available.
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = true;
        // Notify producer that
        // status has changed.
        notifyAll();
        return message;
    }

    public synchronized void put(String message) {
        // Wait until message has
        // been retrieved.
        while (!empty) {
            try { 
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = false;
        // Store message.
        this.message = message;
        // Notify consumer that status
        // has changed.
        notifyAll();
    }
}