【问题标题】:Programatic SauceLabs Tunnel control程序化 SauceLabs 隧道控制
【发布时间】:2016-02-26 01:34:40
【问题描述】:

是否有人有任何以编程方式控制 SauceLabs SauceConnect 隧道以进行 Selenium WebDriver 测试的经验?特别是在 Java 代码中。 SauceLabs 文档中的示例假设在执行测试之前手动创建了一个隧道,或者在某处有一个永久隧道供所有测试使用。

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

【问题讨论】:

    标签: selenium-webdriver saucelabs


    【解决方案1】:

    我做了一些研究,并想出了一种从代码中控制我的 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);
    

    【讨论】:

      【解决方案2】:

      以下是我编写的一系列 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();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-03-27
        • 2021-10-19
        • 2020-01-10
        • 2021-05-02
        • 2021-09-23
        • 1970-01-01
        • 2010-09-27
        • 2014-10-31
        • 1970-01-01
        相关资源
        最近更新 更多