Архив метки: GPS

А вы знали, что в Windows есть пять вариантов переключения языка?

Да, именно столько способов имеются в современных ОС Windows (начиная с «десятки»). Кроме того, с помощью специальных программ можно повесить переключение раскладов на любую клавишу или сочетание клавиш.
Читать

Прокачиваем WebDriverAgent, или как тестировать iOS-приложения после ядерного взрыва. Расшифровка доклада

Когда Apple с выходом Xcode 8 отказались от UI Automator, мы, как и многие, оказались у разбитого корыта. Appium, который у нас использовался, потерял актуальность, мы начали искать альтернативы и нашли инструмент WebDriverAgent от Facebook. Под катом — текстовая расшифровка доклада о том, с какими проблемами мы столкнулись, как мы их решали и как это повлияло на нашу инфраструктуру тестирования iOS-приложений.

Avito — это несколько веб-интерфейсов, API и приложения под iOS и под Android. Всё это надо тестировать, поэтому у нас есть свой тестовый фреймворк. Он выглядит примерно так:

И состоит из двух основных частей.

Первая — это, конечно, сами тесты. Они представляют собой набор высокоуровневых шагов: залогиниться, открыть страничку, разлогиниться и так далее. Работают они со стейджами, где описана работа со всеми конкретными элементами: нажать на кнопку, заполнить поле ввода и так далее. И (куда же без них?) под этим всем скрываются page objects, в которых описаны сами элементы, их локаторы, описания.

Вторая часть фреймворка — большой набор библиотек, которые позволяют создать объявление, найти готовое, удалить его, что-то сделать с пользователем, еще что-то, применить какие-то услуги и так далее. Короче, все, что нужно для получения предсостояния теста.

Все тесты «общаются» с тестируемыми приложениями, кроме API, через WebDriver протокол. Для iOS мы использовали Appium. Всё было круто: у нас было тестовое покрытие, всё работало. А потом Apple объявила о выходе Xcode 8. Ключевая его особенность в том, что они полностью отказались от UI Automation, который был в инструментах Apple. И вся эта схема просто перестала работать.

Надо было принимать какое-то решение. Apple предлагает взамен писать тесты на Swift. Но мы не хотим: у нас большой набор библиотек, 200 тысяч строк кода, 400 тестов под разные приложения и так далее. Не хочется всё это терять. В качестве замены мы нашли инструмент от Facebook — WebDriverAgent. Система его работы похожа на Appium, он тоже поднимает веб-сервер, правда, сразу на девайсе или на симуляторе, и транслирует вызовы из тестовых скриптов через XCUITest на тестируемое приложение.

Плюсы WDA

Что умеет WebDriverAgent? Поддерживает Json Wire протокол. Это значит, что мы сохраняем свои тесты и весь тестовый фреймворк. Под капотом у него XCUITests. Это круто, потому что это технология, которую поддерживает Apple, и есть шанс, что еще года два-три она продержится. Написан WebDriverAgent на ObjC, благодаря этому нам не нужно переписывать всё с каждым новым релизом Xcode.
Также из плюсов: WDA поддерживает различные стратегии локаторов, можно искать по типу элемента, по имени, XPath — все как мы любим. Из дополнительных плюсов – позволяет работать с системой вне приложения. Мы можем сходить в настройки, в Safari, вбить DeepLink, открыть сразу приложение, где нам надо и так далее. В настройках запретить или разрешить геолокацию и так далее. Ещё WDA поддерживает технологию Touch ID, это отдельный плюс этого инструмента.

Минусы WDA

Есть и минусы. Первый — Инспектор. В любой системе функционального тестирования он занимает очень важную роль, это то, как автотестер видит приложение, код страницы на экране и так далее. У WebDriverAgent с этим довольно плохо. Конкретнее расскажу об этом ниже.
И ключевая проблема — в нашей инфраструктуре он работает медленно. Причем настолько, что им нельзя пользоваться. Но это всё мы узнали не сразу. Сначала было всё круто, но не заработало примерно у половины наших автотестеров.

Первая правка, которую мы внесли в WebDriverAgent была вот такой.

Ребята из Facebook не запаривались: ведь все знают, что в MacOS — регистронезависимая файловая система. Но у нас в отделе мы пишем код, который выполняется преимущественно на Linux-серверах, поэтому мы сразу советуем всем переставить MacOS, и кто так сделал, не смогли скомпилировать WDA: просто перепутали букву.

Следующая смешная правка, которую мы внесли, выглядела так.

Это — метод стирания текста из поля. Правка ускорила некоторые наши тесты примерно на 30 секунд. Почему? Основная проблема в том, что длина итерируемого массива вычислялась прямо в теле цикла. Обычно в компилируемых языках вам не нужно об этом думать: там есть оптимизатор, он делает всё за вас. Но в системах функционального тестирования это не работает. Потому что у нас есть элемент на экране, который лежит где-то в кэше WDA, и который надо достать оттуда, найти на странице, взять его атрибуты, найти среди них value и стереть один символ. Потом снова пойти, снова достать элемент из кэша, снова найти его на странице, снова вычислить длину поля value и снова стереть один символ. У нас поле описания, по-моему, 1000 символов. Минус 30 секунд.
Но и это не самое страшное. Самое страшное выглядело примерно так:

Здесь — страница «Уточнить» в поиске в iOS-приложении Avito. Он осуществляется через XPath. Ищутся два элемента: минимальная цена и максимальная. Это видео ускорено в 6,5 раз. Реальное время прохождения — минута сорок. Из них 20 секунд я вожу руками по экрану, набиваю текст и так далее. По 40 секунд выполняется каждый из двух запросов на поиск элементов. При этом «сжираются» лишние 16 Мб оперативной памяти. Мы подумали и поняли, что жить с этим нельзя: сейчас это занимает 40 секунд, а если пройдет половина теста, наберется еще памяти, запросы начнут выполняться еще дольше. И либо WebDriverAgent упадет, когда использует слишком много памяти, либо у нас HTTP-запросы будут отваливаться по таймауту.

Мы посидели, посмотрели, что мы можем с этим сделать, и нашли решение: придумали свою систему аллокаторов. Назвали её XUI: eXtended UI Interator. Это просто хорошо отражало наше отношение к этому на тот момент. Под её капотом — биндинги на XCUI локаторы. Вот второе демо:

Ищутся ровно те же элементы, но теперь через наши новые локаторы. Видео ускорено в два раза, реальное время его прохождения — 20 секунд, и это те же 20 секунд, что я вожу руками по экрану, потому что каждый запрос выполняется меньше, чем за одну секунду. И используется всего 1,5 Мб памяти.

Как мы этого добились? Если кто-то писал тесты на XCUI нативно, то он знает, что там всё начинается с того, что у нас есть объект application, из которого мы потом ищем просто элементы, например, текст, кнопка с надписью on и так далее. Если что-то посложнее – можно найти элемент по индексу и так далее.

XCUI селекторы:

  • let app = XCUIApplication()
  • app.staticTexts[«Volley»]
  • app.buttons[«On»]
  • app.windows.element(boundBy: 0)

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

  • app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element.children(matching: .other).element(boundBy: 0).children(matching: .other).element(boundBy: 1).children(matching: .other).element.children(matching: .other).element(boundBy: 0).children(matching: .other).element.children(matching: .button).element.tap()

Но суть остается такой же: есть какой-то parent, у него есть либо прямой потомок из какого-то типа, либо, если вглубь по дереву, то непрямой.
Поэтому нам нужно знать тип элемента и иметь какой-то признак прямого и непрямого потомка. Мы залезли в WebDriverAgent, начали кодить. Взяли тип элемента. Если нам тип не важен — просто *. Точка показывает, прямой или непрямой потомок, children ищем или descendants.

Всё круто, но это всё может быть вложено, поэтому нам нужен какой-то разделитель. Мы выбрали pipe (“|”), просто чтобы не путаться с XPath.

И всё, что было выше, выполняется теперь в цикле.

С этим разобрались. Дальше нужен выбор по индексу элемента. Если пришел индекс, если мы задетектили, что он нам нужен, выбираем просто из коллекции найденных элементов. Здесь есть ключевое слово last, чтобы брать сразу последний.

Самое важное — нам еще нужен выбор по сложным условиям, потому что у XPath есть XPath access и куча других функций. И тут нас сильно выручил NSPredicate, класс, который поставляется в Foundation Framework Apple и служит для фильтрации и выборки элементов из коллекции.

По сути, он умеет очень много. Тут ссылка, можно почитать.

Кто помнит, в Appium во времена UI Automation были BEGINSWITH, MATCH и так далее, это прямо оно. Синтаксис – что-то среднее между RegExr и секцией WHERE в SQL.
Мы это скомпоновали и получились такие локаторы. В круглых скобках — NS предикаты, в квадратных — индексы, пайпы и точки.

XUI-локаторы

  • xui=StaticText[1]|TextView[0]
  • xui=Button(label == 'Stop')
  • xui=NavigationBar[last]
  • xui=Table[0]|Cell[3]|.StaticText(id=Address)
  • xui=Table[0]|Cell[3]|.StaticText(id=Time)

С этим разобрались: заработало с приемлемой скоростью, начали разбираться со следующей проблемой. Инспектор. У WebDriverAgent инспектор есть, есть даже классная инструкция, как его запустить, можно выполнить раз команду, два, три и… Не завелось, короче:

Даже если бы завелось, там тоже ничего полезного. Поэтому нам пришлось написать свой. Он тоже простенький, но довольно функциональный.

Есть дерево элементов, зеленым отмечены accessibilityID, если они проставлены. Можно выбрать элемент и в правом верхнем углу можно увидеть информацию по нему, в левом — на скриншоте посмотреть, где конкретно он расположен на экране. Ключевое, что было нам нужно — строка поиска, чтобы тестировать, правильно мы составили локаторы или нет, ищется по нему элемент на странице или нет.

Что в итоге? Был WebDriver со своими плюсами и минусами. Мы над ними немного поработали —
стало сильно лучше.
На нашей схеме Appium поменялся на WebDriverAgent:

Тесты мы оставили как есть:

Стейджи оставили как есть. В page objects поменяли локаторы Appium на наши. Они стали даже более читаемыми.

На всё ушло примерно 2-3 недели и сэкономило нам 200 000 строк в библиотеках и примерно 400 тестов.
На этом мы не остановились, конечно.
Appium помимо того, что просто позволял тестировать, решал еще какие-то задачи. Теперь нам пришлось заниматься ими самим. Например: мы хотим параллелить тесты, нам нужно запустить несколько инстансов WebDriverAgent, запустить тесты, в каждом указать API и так далее.

Но ведь люди придумали grid! Однако вот проблема: когда подключаешь больше трех нод, ему сносит башню, он начинает жрать память, течь, тупить. Некоторые люди извращаются примерно вот так. Не пытайтесь здесь ничего понять:

Здесь есть grid, за ним еще 4, за ними еще 4 и так далее. Я посчитал: с учетом особенностей нашего проекта нам бы пришлось бы сто таких гридов ставить, целый сервер на это выделять. Мы не стали этим заморачиваться. Мы написали свой. Он простой, написан на Go, использует примерно 10 Мб оперативной памяти и обслуживает 300 нод. Регистрирует ноды, проксирует все вызовы на эти ноды и выбирает подходящую с учетом capabilities на запрос сессии. И в конце надо ее освободить. Либо когда сессию закрыли, либо по тайм-ауту, если тест свалился и не смог сообщить, что закончил. Всё это совместимо с Selenium grid, чтобы можно было работать. Теперь у нас есть вот эта схема, она рабочая на этот раз:

Но проблемы наши на этом не заканчиваются, потому что мы тестируем приложение, надо его на телефон как-то поставить, что-то с ним сделать, поэтому мы написали штуку под названием grid-wda-agent:

Она делает несколько простых вещей: регистрируется в grid, потому что сам WebDriverAgent этого не умеет. Проксирует все вызовы на WDA, и на старт сессии выбирает или запускает нужный симулятор, который мы запросили в Capabilities, удаляет старую версию приложения, ставит новую, и перезапускает WDA, если надо, потому что он все равно иногда поджирает память, его лучше иногда рестартовать. И дополнительно он записывает видео прохождения теста и отправляет его потом в хранилище по S3 протоколу.
Всё это мы положили на Github в организацию qa-dev, можно заходить, читать, слать pull requests, issues.

Что теперь делать, если в 2018 году мы хотим тестировать iOS-приложения?
Два пути. Если умеем Swift/ObjC — мы можем писать нативные тесты, XCUI Tests, ждать, пока это будет компилироваться, чтобы проверить один тест. Или можем взять WebDriverAgent. Тут выбор побольше: есть либо Appium, разработчики которого спустя полгода спустя выхода XCode 8 все-таки запилили свою реализацию поверх WDA Agent, форкнув его и что-то добавив. Есть оригинальная версия от Facebook. И наша.

Что есть у Facebook? Он официальный, в него все потихоньку контрибутят.
Есть Appium. Во-первых, процитирую Дэна, который Appium разрабатывает.

«De-facto standard for automating mobile applications».

Во-вторых, у Appium есть поддержка open-source сообщества. И ребята недавно запилили свой инспектор в виде приложения для MacOS.

Есть наш вариант. У нас есть быстрые локаторы, свой инспектор, Grid, Grid-агент. Последние три, в принципе, работают с любым WebDriverAgent, можно их брать отдельно и пробовать использовать.

Какие выводы мы можем сделать?
Первое — выбирайте то, что подходит вам. Второе — не бойтесь делать свои инструменты. В 2015 году моя жизнь выглядела примерно так:

Потом в 2016 году начали происходить все эти события, мы начали писать свою «костылевую» версию WDA. Стало так:

Сейчас у нас есть Grid, Grid-агент… Жизнь-то налаживается!

Когда делаете свои велосипеды, делайте то, что нужно вам для проекта. И более важно – не делайте то, что не нужно. Потому что когда вся история происходила, мы думали: «классно, есть XPath, мы сейчас его возьмем и сделаем нормально, а не как у них, и будет работать». Но XPath имеет настолько мощный синтаксис и настолько много функций, что мы, наверное, бы до сих пор писали, если бы пошли этим путем. Нам нужны были быстрые локаторы, а не сделать XPath.
Пожалуй, на этом всё. Задавайте свои вопросы.


→ Посмотреть доклад на видео

Let's block ads! (Why?)

Автор: Bone Guerra

DuckDuckGo: Жизнь без сервисов Google

Если честно, я не считаю Google корпорацией зла. Возможно, я пока просто не дошёл до нужной степени паранойи, ибо большинство специализирующихся на проблемах приватности компаний снова и снова призывают пользователей отказаться от сервисов Google. Наверное, действительно не стоит хранить все яйца в одной корзине, и разумно было бы перенести часть своей цифровой жизни в какие-то другие сервисы.

Автор: Alex Semёnov-Sherin
Дата публикации: 2018-01-29T12:40:00.000+07:00

DuckDuckGo: Миф о приватности №1. Всем плевать на конфиденциальность!

Существует множество мифов, связанных с вопросами приватности. И команда конфиденциального поисковика DuckDuckGo решила разобраться, какие из них выдумка, а какие действительно могут оказаться правдой. Перевод заметки из официального блога компании. Мифы о приватности:№1. Вопросы конфиденциальности мало кого волнуют Когда вы в последний раз говорили с кем-то о проблемах конфиденциальность

Автор: Alex Semёnov-Sherin
Дата публикации: 2018-01-14T22:12:00.000+07:00

PyPDF2: новый форк pyPdf (Перевод)

Сегодня узнал, проект pyPDF НЕ умер, как я думал раньше. На самом деле он перешёл в PyPDF2 (видите небольшую разницу в написании?). Есть так же вероятность того, что кто-то занимается и оригинальной версией проекта. При желании, за судьбой PyPDF2 можно следить на reddit. Тем временем я решил посмотреть, чем же PyPDF2 отличается от оригинала. Так что если у Вас есть пара свободных минут — милости просим.

Введение в PyPDF2

Два года назад я уже  писал про pyPDF, а совсем недавно мне как раз пришлось погрузиться в различные библиотеки для работы с PDF на Python, так что появление нового форка пришлось кстати. Мы возьмём несколько моих старых примеров, запустим их в PyPDF2 и посмотрим, как они сработают.
# Соединяем два PDF
from PyPDF2 import PdfFileReader, PdfFileWriter
 
output = PdfFileWriter
()
pdfOne = PdfFileReader
(open( "somepathtoaPDf", "rb"))
pdfTwo = PdfFileReader
(open("someotherpathtoaPDf", "rb"))
 
output.
addPage(pdfOne.getPage(0))
output.
addPage(pdfTwo.getPage(0))
 
outputStream =
open(r"output.pdf", "wb")
output.
write(outputStream)
outputStream.
close()
На моём Windows 7 это сработало без проблем. Как Вы можете предположить, всё, что этот код делает — создаёт два объекта PdfFileReader и прочитывает в каждом первую страницу. После этого, он добавляет эти две страницы в наш PdfFileWriter. И, наконец, мы открываем новый файл и записываем в него PDF. Вот и всё! Мы только что создали новый документ из двух разных PDF!
Теперь давайте попробуем скрипт для поворота страницы из другой моей страницы:
from PyPDF2 import PdfFileWriter, PdfFileReader
 
output = PdfFileWriter()
input1 = PdfFileReader(file("document1.pdf", "rb"))
output.addPage(input1.getPage(1).rotateClockwise(90))
# output.addPage(input1.getPage(2).rotateCounterClockwise(90))
 
outputStream = file("output.pdf", "wb")
output.write(outputStream)
outputStream.close()
Этот скрипт так же сработал на моей машине. Очень даже хорошо. Мой последний тест на соответствие должен проверить, можем ли мы вытаскивать данные так же, как мог оригинальный pyPdf. Давайте попробуем прочитать метаданные:
>>> from PyPDF2 import PdfFileReader
 
>>> p = r'C:UsersmdriscollDocumentsreportlab-userguide.pdf'
 
>>> pdf = PdfFileReader(open(p, 'rb'))
 
>>> pdf.documentInfo
 
{'/ModDate': u'D:20120629155504', '/CreationDate': u'D:20120629155504', '/Producer': u'GPL Ghostscript 8.15', '/Title': u'reportlab-userguide.pdf', '/Creator': u'Adobe Acrobat 10.1.3', '/Author': u'mdriscoll'}
>>> pdf.getNumPages()
 
120
>>> info = pdf.getDocumentInfo()
 
>>> info.author
 
u'mdriscoll'
>>> info.creator
 
u'Adobe Acrobat 10.1.3'
>>> info.producer
 
u'GPL Ghostscript 8.15'
>>> info.title
 
u'reportlab-userguide.pdf'
Тоже очень хорошо, кроме поля автора. Я определённо не автор этого документа и я не знаю, почему PyPDF2  решил, что я им являюсь. Всё остальное сработало верно. Теперь давайте посмотрим, что тут нового.

Что нового в PyPDF2

Одна из вещей, которую я сразу заметил как только взглянул на исходники PyPDF2, это то, что он добавляет несколько новых методов в PdfFileReader и PdfFileWriter. Кроме того, я заметил, что появился совершенно новый модуль merger.py, который содержит класс PdfFileMerger. Поскольку никакой нормальной документации нет — придётся лезть «под капот». Единственный новый метод, который был добавлен в reader — это getOutlines, который позволяет получить эскизы документа (outlines), если они есть. В writer появилась возможность добавить bookmarks и named destinations. Не так уж и много, но дарёному коню в зубы не смотрят. Но больше всего меня вдохновил новый класс PdfFileMerger, который мне напомнил почти погибший проект Stapler. PdfFileMerger позволяет нам объединить несколько PDF в один, используя соединение, вставку и вырезку и их комбинации.
Давайте попробуем?
import PyPDF2
 
path = open('path/to/hello.pdf', 'rb')
path2 = open('path/to/another.pdf', 'rb')
 
merger = PyPDF2.PdfFileMerger()
 
merger.merge(position=0, fileobj=path2)
merger.merge(position=2, fileobj=path)
merger.write(open("test_out.pdf", 'wb'))
Тут мы соединяем два файла вместе. В первый файл вместо третьей страницы будет добавлен второй файл, а как только содержание второго файла закончится будет продолжен первый файл. Это проще, чем проходиться по страницам двух документов и соединять их. Команда merge имеет следующую сигнатуру и строку документации, которая хорошо всё это описывает:
>>> merge(position, file, bookmark=None, pages=None, import_bookmarks=True)
 
Сливает страницы из исходного документа, определённого в
"file" в файл вывода на место, определённое в "position".
 
Опционально Вы можете определить закладку, которая будет установлена на начало этого файла. Для этого надо передать текст закладки в параметре
"bookmark".
 
Вы можете запретить импорт закладок из исходного документа если передадите значение False параметру
"import_bookmarks".
 
Вы можете так же использовать параметр "pages" для того, чтобы добавить только некоторые страницы исходного файла. Для этого надо задать их диапазон.
Есть так же метод append, который аналогичен команде merge, но вместо того, чтобы сливать файлы, он добавляет их в конец исходного файла. Вот скрипт для примера:
import PyPDF2
 
path = open('path/to/hello.pdf', 'rb')
path2 = open('path/to/another.pdf', 'rb')
 
merger = PyPDF2.PdfFileMerger()
 
merger.append(fileobj=path2)
merger.append(fileobj=path)
merger.write(open("test_out2.pdf", 'wb'))
И совсем не больно!

Подводим итоги

Мне кажется, это хорошая альтернатива для работы с PDF. Я могу комбинировать и разделять PDF при помощи PyPDF2 даже проще, чем с оригинальным pyPdf. Кроме того, я надеюсь, что PyPDF не умрёт, так как у него есть спонсоры, оплачивающие его разработку. Согласно потокам reddit есть даже шанс, что оригинальный pyPdf будет переработан и в конце концов оба эти проекта сольются в дружеском экстазе. Но, вне зависимости от того, чем всё закончится, я рад, что разработка его возобновилась и надеюсь, что она не скоро прекратится.

Домашнее чтение

Автор: Ishayahu Lastov

Acme

Как вы думаете, каким текстовым редактором пользуются разработчики языка программирования Go Роб Пайк и Расс Кокс, разработчик Unix Кен Томпсон? Чем пользовался Деннис Ритчи? Emacs? Vim? Какая-то IDE? А вот и нет. Ответ: Acme. Да-да, представьте себе, есть такой редактор. Его написал еще в девяностых Роб Пайк для операционной системы Plan9 на языке Alef. Позже редактор портировали на C.

Если кратко, то это такая среда для программистов. Упор сделан на текстовое взаимодействие и управление мышкой. Любой фрагмент текста может быть исполнен как команда. Редактор полностью тайловый, однако это отдельное приложение, а не полноценный оконный менеджер как i3 или Awesome.

В качестве демонстрации возможностей редактора Расс Кокс записал скринкаст посвященный Acme. Выглядит очень впечатляюще, учитывая, что этим редактором он пользуется ежедневно. По словам Расса, все коллеги, только взглянув на монитор, могут сразу сказать, что это его компьютер: подводит желтый фон редактора и голубые панели. Кстати, в Acme нет подсветки синтаксиса. Роб Пайк и пользователи Acme считают это бесполезной и отвлекающей вещью.

Загрузить и поиграться с Acme можно тут.

Автор: Roman Brovko