Java-клиент для FlexMonkey, или Java-style LocalConnection



Думаю, во многих командах, так или иначе связанных с разработкой Flex-приложений, рано или поздно возникает вопрос об автоматизированном тестировании продукта. А так, как наша команда занимается разработкой AIR-клиента для online-покера, совершенно закономерно, такой вопрос возник и у нас.

Сначала он прорабатывался исключительно QA-командой, ими были рассмотрены некоторые инструменты, включая FlexMonkey. В частности, не была оставлена без внимания данная статья на Хабре.

Цикл тестирования включает в себя добавление стола через админку, процесс регистрации на сайте с последующим скачиванием, установкой и запуском клиента. Для этого на Java были написаны Selenium Test case-ы. Что делать дальше было непонятно, так как стандартный FlexMonkey плагин для Selenium — FlexMonkium — умеет работать только с Flex-приложениями, работающими в браузере, во Flash-плагине, потому что написан на JS и взаимодействует с Flash-кой через ExternalInterface, который отсутствует в AIR Runtime. В свою очередь, стандартная консоль FlexMonkey взаимодействует с любым Flex-приложением, включая AIR, через чисто Flash-овую технологию LocalConnection, Java-реализации которой до этого не существовало. Теперь она существует.

Было принято решение писать клиент к FlexMonkey на Java, проведен эстимейт и я взялся за работу. Сразу оговорюсь, что 19 июля сего года, уже после завершения основных работ над нашей библиотекой, вышло новое поколение фреймворка FlexMonkey (теперь он называется MonkeyTalk) и в нем, после беглого ознакомления, проблемы с «кросс-технологичностью» вроде бы были устранены, путем организации коннекта клиент-агент через сокеты, но я доволен приобретенным опытом и думаю, что мы будем и дальше развивать этот продукт на базе старой архитектуры от GorillaLogic.

Пролог

Итак, в первую очередь нужно было разобраться, что же такое LocalConnection, и как с ним работать. Во всей Сети есть только один источник, в котором описаны, пусть и не в полной мере, внутренности LocalConnection, и информация там немного устаревшая. Вот эта статья.

Что мы можем понять из данной статьи? Что LocalConnection — это Memory File Mapping объект, размером 65535 байт, имеющий в системе имя MacromediaFMOmega, эксклюзивный доступ к которому обеспечивается путем захвата мютекса с именем MacromediaMutexOmega. Вот примерная карта данной области памяти:



Как видно из карты, в данном протоколе во всю используется AMF кодирование обеих версий. AMF (Action Message Format) — протокол бинарного представления данных, разработанный Adobe. Спецификация на AMF0 доступна здесь, на AMF3 здесь.

В общем случае процесс получения сообщения выглядит так:
  • 1. Регистрируем листенер, путем добавления его имени в цепочку
  • 2. Захватываем мютекс
  • 3. Получаем file mapping и проверяем получателя
  • 4. Если мы получатель, читаем сообщение и обнуляем timestamp и длину, таким образом мы маркируем сообщение как прочитанное
  • 5. Повторяем бесконечно, начиная с шага 2

Процесс записи сообщения выглядит так:
  • 1. Захватываем мютекс
  • 2. Получаем file mapping и проверяем, существует ли получатель, которому мы должны отправить сообщение
  • 3. Записываем сообщение
  • 4. Отпускаем file mapping и мютекс

Java part

Так как в стандартной библиотеке Java отстуствуют нормальные средства для работы с Memory File Mapping, было принято решение писать нативную JNI-библиотеку для работы с данной технологией. Библиотека была написана на C++ в Visual Studio Express 2010 и собрана под 32-х битную архитектуру. Она содержит методы для создания/захвата/отпускания мютекса, создания/получения/отпускания file mapping объекта и записи/чтения в/из него, и враппер над WinAPI функцией GetTickCount(), которая нужна для получения timestamp-а.

Далее был написан Java-класс LocalConnection, который полностью повоторяет интерфейс своего Flash-ового собрата, за исключением событий. У него есть метод setClient(), который принимает экземпляр класса, реализующего интерфейс LocalConnectionSink, в котором определен метод onInvoke(String method, Object… args), собственно принимающий входящие вызовы по имени целевого метода с его параметрами. Метод send() повторяет таковой из Flash-а, принимает имя коннекшна, имя метода, который нужно вызвать, его параметры и помещает все это в очередь на отправку.

В самом классе работает отдельный поток, который в цикле добавляет листенер/читает сообщение, проверяет получателя/записывает сообщение. При поступлении нового сообщения — дергает метод onInvoke() клиента. В качестве сериализатора/десериализатора в/из AMF используется соответствующая библиотека из BlazeDS. Все достаточно просто.

Далее нам нужно было наладить сообщение с automation-агентом FlexMonkey в нашем приложении. Он подключается к проекту путем добавления SWC-библиотеки и входной точкой в нем служит класс MonkeyLink, собственно так же называется и сам протокол.На стороне агента он регистрирует endpoint с именем "_agent", клиент, в свою очередь должен зарегистрироваться под именем "_flexMonkey". Этот класс содержит несколько public-методов. Метод «ping» служит для симметричного пингования клиента/агента раз в полсекунды. Во время его вызова устанавливается флаг isConnected, который свидетельствует о том, что противоположная сторона еще жива и принимает сообщения. По таймеру, раз в 5 секунд, этот флаг сбрасывается.

Так же этот класс содержит ряд методов, которые принимают экземпляры наследников класса MonkeyRunnable. Это команды, представляющие собой экшены, которые мы видим на панели инструментов классической консоли FlexMonkey.

Исходя из этого, был разработан Java-аналог этого класса, и Java-аналоги команд FlexMonkey из ActionScript. Это такие команды, как SetProperty, CallFunction, VerifyProperty, UIEvent и т.д. Данный класс содержит метод playCommand(), который принимает экземпляр команды с нужными параметрами, сериализует ее и отправляет агенту посредством LocalConnection.

Так же данный класс содержит 2 дополнительных потока — первый раз в полсекунды отправляет ping агенту, а второй раз в 5 секунд сбрасывает флаг isConnected.

Над ним сделана обертка в виде класса FlexMonkeyAutomator, который предоставляет в распоряжение QA-инженера простой API для синхронного вызова экшенов на агенте. Также можно указывать количество попыток вызова экшена и задержку между ними. В общем случае сеанс работы с тестируемым приложением выглядит так:

MonkeyLink monkeyLink = new MonkeyLink();
if (monkeyLink.startLink(2000)) { // Соединяемся с агентом, таймаут 2 секунды
    FlexMonkeyAutomator flexMonkeyAutomator = new FlexMonkeyAutomator(monkeyLink);
    flexMonkeyAutomator.setProperty(...
    flexMonkeyAutomator.storeValue(...
    flexMonkeyAutomator.uiEvent(...
    flexMonkeyAutomator.verifyProperty(...
    flexMonkeyAutomator.callFunction(...
 
    monkeyLink.disconnect();
}


Все методы FlexMonkeyAutomator содержат перегруженные версии, принимающие таймаут последним параметром. Это полезно, в частности в таком случае: в последнем экшене мы нажимаем на кнопку выхода из приложения, приложение закрывается и не успевает отправить результат экшена, в данном случае обычная версия метода вызова экшена никогда не вернет управление и наш тест-скрипт не завешится, а вот версия с таймаутом завершится благополучно.

Все исходники данной библиотеки доступны на BitBucket. Прошу не воспринимать всерьез проект JMonkeyLinkTest, который содержит тестовое Swing-приложение с обилием говнокода — оно предназначено исключительно для ситуативного тестирования отдельных фич библиотеки, а основное тестирование проводят наши QA-и на боевых скриптах.

PS: Совсем забыл. Не смотря на то, что при серализации команд на стороне Java, я присваиваю им FQDN, соответствующий их FQDN в ActionScript, они почему-то все равно десериализуются в обычный Object, поэтому в тестируемом приложении их нужно регистрировать через registerClassAlias(), вот так:
registerClassAlias("com.gorillalogic.flexmonkey.monkeyCommands.CallFunctionMonkeyCommand", CallFunctionMonkeyCommand);

Ну, и надеюсь, данный продукт принесет кому-то пользу, а также будет полезен тем, кто хочет построить взаимодействие между Java- и Flash/Flex-кодом в своем проекте посредством LocalConnection. Спасибо за внимание.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 7

    0
    жаль что никто пока не оценил объём проделанной работы. А сколько времени, если не секрет, ушло на ресёч?
      0
      Копался в дампах и дебажил дня 4, когда время свободное от основных тасков появлялось. Потом недели 2.5 непосредственно кодинга.
      Правда, я этой темой еще года 3 назад интересовался, когда писал питоновский модуль — Flash-проектор на основе ScreenWeaver, но в итоге наладил коммуникацию через ExternalInterface, там это можно было сделать, так как NPAPI браузерный плагин использовался. Вот, кстати, этот проект на GC.
      0
      Отличная статья, молодцы, что нашли время на публикацию.
        0
        это наверное самый сложный путь, который только можно было выбрать. Флексманки просто обертка над родной автоматизацией флекса, если заглянуть в его сорцы там будет 1 класс, который и предоставляет видимые селениуму методы, а это значит, что можно было было бы пересобрать библиотеку, дополнив ее сокетами без проблем. Все можно было сделать на чистом флексе/аире, более того можно было бы и клиента поправить под сокеты. Код там не очень, но более менее понятно. Строго говоря, можно было бы просто написать своего клиента на аире используя тот же localconnection и клиент флексманки в качестве примера.

        Что касается статьи «Автоматизация Flex-приложений при помощи Java + Selenium + FlexMonkium», из нее я понял, что проще написать программу(и написал), которая будет читать и выполнять в селениуме тесты прямо в родном xml-формате флексманки, плюс поправил саму библиотеку, чтобы видеть больше информации в случае неудачи. Теперь тестеры могут писать тесты во флексманки и выполнять их без проблем
          0
          Не совсем понял, что Вы имели в виду.
          Написать Flex-овый клиент, который будет крутиться в браузере, получать от FlexMonkium команды, которые будет передавать через сокет AIR-приложению? ИМХО, это более громоздко и ненадежно, так как добавляет в цепочку браузер + JavaScript + еще одно Flex-приложение. Очень толстая цепочка получается, как по мне. Единственный плюс в такой схеме, это то, что она гипотетически кроссплатформенная, что на практике может оказаться далеко не так.
            0
            да нет, два варианта:
            1. дописать поддержку сокета в флексманки и прилагу на жабе
            2. только написать прилагу на аире и юзать localconnection(его не только из браузера можно юзать), так же как это делает клиент флексманки(можно наверное прямо из него вырезать все что надо).

            Никакой javascript не будет использоваться ни в первом ни во втором случае.

              0
              1. Такой вариант рассматривался, но был отклонен именно по причине необходимости лезть во внутренности Манки и что-то там дописывать. Хотелось обойтись только Джавой, да и с LocalConnection хотелось все-таки разобраться давно.
              2. Какой смысл писать свой AIR-клиент при наличии родного? Хотя вопрос все равно не в тему, так как нужна была именно Java.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое