2015-04-07 3 views
4

Я могу автоматизировать веб-сайт (заполнение форм и щелчок) с помощью Selenium Webdriver, чтобы сэкономить время для моих пользователей. Я сталкивался досадная проблема, хотя:Как определить, когда браузер закрыт?

Селен, кажется, не поддерживает никаких обработчиков событий для браузеров самих. Когда браузер закрыт driver.quit() - не указан и неиспользуемый водитель остается, который бросает различные исключения. Имея , вы не можете узнать, когда браузер закрыт, я не могу создать новый экземпляр драйвера .

Что мне нужно - это способ уведомления моей программы, когда браузер закрыт.

Пользователь может закрыть браузер по любой причине, которая нарушает мою программу. Пользователь не может снова запускать автоматические задачи без перезапуска приложения. Зная, когда браузер закрыт, я позволю мне позвонить driver.quit() и создать новый экземпляр драйвера, если пользователь захочет запустить его снова.

Проблема еще более усложняется тем фактом, что когда браузер умирает, ошибка, вызванная ошибкой, неравномерна в браузерах. В Firefox я могу получить исключение UnreachableBrowserException с помощью Chrome NullPointer и WebDriverExceptions.

Чтобы уточнить, я знаю, как закрыть драйвер и браузер, но я не знаю, когда они закрыты внешними источниками. Можно ли это сделать в Selenium 2 в кросс-браузере (и если да, как) или мне нужно найти другой способ (например, другую библиотеку) для просмотра окна браузера?

+0

Поскольку вы не можете использовать 'driver.close()' или 'driver.quit()', вы можете проверить, работает ли ваш браузер Ex. chrome.exe больше не существует. Просто предложение. Посмотрите этот блог об обнаружении процесса выхода - https://beradrian.wordpress.com/2008/11/03/detecting-process-exit-in-java/ – LittlePanda

+0

Спасибо, я попробую это. Фрагмент выглядит очень полезным. – Shantu

ответ

4

Я решил проблему с помощью JNA (Java Native Access) 3.4, который уже был включен в Selenium. Моя целевая платформа - только Windows, но для этой кросс-платформенной работы не стоит много работать. Я должен был сделать следующее:

  1. собрать все идентификаторы процессов браузера перед запуском WebDriver (это можно сделать с помощью утилиты, как tasklist или PowerShell Get-Process). Если вы точно знаете, что перед запуском приложения не будет запущенных процессов браузера, этот шаг можно опустить.
  2. Инициализация драйвера.
  3. Соберите все процессы браузера снова, а затем выберите разницу между ними.
  4. Используя код, связанный с комментарием LittlePanda в качестве базы, мы можем создать службу наблюдения в новом потоке. Это предупредит всех слушателей, когда все процессы будут закрыты.
  5. Метод Kernel32.INSTANCE.OpenProcess позволяет нам создавать HANDLE объекты из pids.
  6. С помощью этих ручек мы можем подождать, пока все процессы не будут сигнализированы с помощью метода Kernel32.INSTANCE.WaitForMultipleObjects.

Вот код, который я использовал, так что это может помочь другим:

public void startAutomation() throws IOException { 
     Set<Integer> pidsBefore = getBrowserPIDs(browserType); 
     automator.initDriver(browserType); //calls new ChromeDriver() for example 
     Set<Integer> pidsAfter = getBrowserPIDs(browserType); 
     pidsAfter.removeAll(pidsBefore); 

     ProcessGroupExitWatcher watcher = new ProcessGroupExitWatcher(pidsAfter); 
     watcher.addProcessExitListener(new ProcessExitListener() { 
      @Override 
      public void processFinished() { 
       if (automator != null) { 
        automator.closeDriver(); //calls driver.quit() 
        automator = null; 
       } 
      } 
     }); 
     watcher.start(); 
     //do webdriver stuff 
} 

private Set<Integer> getBrowserPIDs(String browserType) throws IOException { 
    Set<Integer> processIds = new HashSet<Integer>(); 

    //powershell was convenient, tasklist is probably safer but requires more parsing 
    String cmd = "powershell get-process " + browserType + " | foreach { $_.id }"; 
    Process processes = Runtime.getRuntime().exec(cmd); 
    processes.getOutputStream().close(); //otherwise powershell hangs 
    BufferedReader input = new BufferedReader(new InputStreamReader(processes.getInputStream())); 
    String line; 

    while ((line = input.readLine()) != null) { 
     processIds.add(Integer.parseInt(line)); 
    } 
    input.close(); 

    return processIds; 
} 

И код для наблюдателя:

/** 
* Takes a <code>Set</code> of Process IDs and notifies all listeners when all 
* of them have exited.<br> 
*/ 
public class ProcessGroupExitWatcher extends Thread { 
    private List<HANDLE> processHandles; 
    private List<ProcessExitListener> listeners = new ArrayList<ProcessExitListener>(); 

    /** 
    * Initializes the object and takes a set of pids and creates a list of 
    * <CODE>HANDLE</CODE>s from them. 
    * 
    * @param processIds 
    *   process id numbers of the processes to watch. 
    * @see HANDLE 
    */ 
    public ProcessGroupExitWatcher(Set<Integer> processIds) { 
     processHandles = new ArrayList<HANDLE>(processIds.size()); 
     //create Handles from the process ids 
     for (Integer pid : processIds) { 
      processHandles.add(Kernel32.INSTANCE.OpenProcess(Kernel32.SYNCHRONIZE, false, pid)); //synchronize must be used 
     } 
    } 

    public void run() { 
     //blocks the thread until all handles are signaled 
     Kernel32.INSTANCE.WaitForMultipleObjects(processHandles.size(), processHandles.toArray(new HANDLE[processHandles.size()]), true, 
      Kernel32.INFINITE); 
     for (ProcessExitListener listener : listeners) { 
      listener.processFinished(); 
     } 
    } 

    /** 
    * Adds the listener to the list of listeners who will be notified when all 
    * processes have exited. 
    * 
    * @param listener 
    */ 
    public void addProcessExitListener(ProcessExitListener listener) { 
     listeners.add(listener); 
    } 

    /** 
    * Removes the listener. 
    * 
    * @param listener 
    */ 
    public void removeProcessExitListener(ProcessExitListener listener) { 
     listeners.remove(listener); 
    } 
} 

Примечание: при использовании PowerShell этот путь, скрипты профиля пользователя выполняются. Это может создать неожиданный вывод, который нарушает вышеуказанный код. Из-за этого рекомендуется использовать список задач.

+0

очень впечатляет –

+0

Вы можете добавить параметр -NoProfile' в PowerShell, чтобы он не запускал сценарий профиля пользователя. 'String cmd =" ​​powershell -NoProfile get-process "+ ...' –

+0

Как я читал, переключатель -NoProfile отключает только пользовательские профили. Профиль системы все еще выполняется. – Shantu

Смежные вопросы