Запуск трудоемкой автоматизированной схемы тестирования на основе машинного обучения

контрольная работа
Запуск трудоемкой автоматизированной схемы тестирования на основе машинного обучения

задний план

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

  • Чтобы избежать «спортивной» оптимизации производительности, после того, как многие команды вложили много времени и энергии в специальное управление приложениями, из-за отсутствия нормализованных методов контроля и управления, колебания производительности окончательно ухудшились;
  • Данные скрытого онлайн-журнала не могут полностью отражать реальный опыт и чувства пользователя по отношению к приложению;

Одним из наиболее важных показателей, влияющих на взаимодействие с пользователем, является время запуска, особенно при извлечении новых.Как правило, существует два способа измерения времени запуска.Одним из них является использование технологии для закапывания точек, но это трудно записать данные, основанные на технологии. Измерьте реальное чувство тела пользователя (хорошая онлайн-статистика? Но реальное чувство тела плохое?), и невозможно получить данные о конкурентном продукте на основе технических точек отслеживания; другой - передать запись экрана и покадровый тест, но покадровый анализ экрана ручной записи будет искусственным.Ошибка восприятия (непоследовательное распознавание границ конечных битов) и рентабельность инвестиций в непрерывную доставку специальных ручных тестов производительности не высока.Например , запись 10 раз, извлечение ключевых кадров для взятия среднего, занимает около получаса, и чем больше периодов выборки, тем дольше. Поскольку я недавно читал книгу по машинному обучению, мне было интересно, смогу ли я применить этот случай на практике.

До этого я также исследовал аналогичные решения, уже существующие в отрасли: некоторые распознаются по тексту OCR, а некоторые сравниваются по изображениям.Если решение для сравнения изображений представляет собой сравнение целых изображений, рекламные объявления и постеры на главной странице в процессе запуска видео изменятся. , Если это частичное сравнение, то место, где первый экран отображается не полностью после полного запуска приложения, может быть не в одном и том же месте каждый раз, поэтому, обратившись к различным решениям, я объединил свои собственные идеи и все схема была реализована снова, и затем эта схема будет представлена ​​в деталях.

Общий процесс

  • Этап 1 в основном предназначен для сбора данных, преобразования видео в изображения и создания обучающих данных и тестовых данных.

  • Второй этап в основном заключается в обучении модели и оценке качества

  • Третий этап в основном заключается в прогнозировании и расчете времени запуска с помощью обученной модели.

Подготовка окружающей среды

Поскольку я реализовал все решение через Python, мне нужно установить среду Python локально.Здесь я использую компьютер Mac, поэтому среда Python поставляется по умолчанию, но если вы хотите использовать Python3, вам нужно обновить ее самостоятельно, и установить инструмент pip:

brew install pip3

Установите scikit-learn, простую среду машинного обучения, а также зависимый пакет научных вычислений numpy и библиотеку алгоритмов scipy:

pip3 install scikit-learn
pip3 install numpy
pip3 install scipy

Библиотеки обработки изображений OpenCV и imutils:

pip3 install opencv-contrib-python
pip3 install imutils

ffmpeg для кадрирования видеофайлов:

brew install ffmpeg

Установите инфраструктуру airtest (кроссплатформенная среда автоматизации пользовательского интерфейса от NetEase):

pip3 install -U airtest

Установите инфраструктуру poco (кроссплатформенная среда автоматизации пользовательского интерфейса от NetEase):

pip3 install pocoui

Примечание. Переключатель сенсорной обратной связи в параметрах разработчика телефона Android должен быть включен, чтобы можно было точно определить момент нажатия значка приложения.

первый этап

Установить в первый раз

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

Кроме того, для имитации реальных соматосенсорных ощущений пользователя, в первую очередь, необходимо имитировать реальный щелчок пользователя в процессе запуска приложения. В настоящее время приложение не может быть напрямую вызвано через adb. Я использую фреймворк poco для реализации щелчок по значку настольного приложения.

poco = AndroidUiautomationPoco()
poco.device.wake()
poco(text='应用名字').click()
poco(text='下一步').click()
poco(text='允许').click()
poco(text='允许').click()
poco(text='允许').click()
os.system("adb shell am force-stop {}".format(package_name))

Начать запись экрана

Используйте команду adb, чтобы запустить службу записи экрана, -time-limit 20 означает, что запись экрана длится 20 секунд. При нормальных обстоятельствах запуск и домашняя страница могут быть завершены в основном за 20 секунд. Если это на слабом компьютере , время может быть соответствующим образом увеличено.

Запись экрана запускается отдельным потоком.

subprocess.Popen("adb shell screenrecord  --time-limit 20 /sdcard/sample.mp4", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

запустить приложение

Установите тестируемое приложение перед тестом, а затем, щелкнув всплывающее окно разрешения, завершите процесс и щелкните значок на рабочем столе, чтобы запустить приложение.

os.system("adb install -r {}".format(apk_path))
poco(text="应用名字").click()

После завершения записи экрана завершите процесс, а затем повторите описанный выше процесс запуска и повторите несколько раз в зависимости от частоты дискретизации.

os.system("adb shell am force-stop {}".format(package_name))

кадрирование видео

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

os.system("adb pull /sdcard/sample.mp4 {}".format(video_local_path))
os.system("ffmpeg -i {} -r 60 {}%d.jpeg".format(video_local_path, test_path))
-r 指定抽取的帧率,即从视频中每秒钟抽取图片的数量。60代表每秒抽取60帧。

Извлечение обучающего набора и данных тестового набора

Обычно мы делим данные на обучающий набор и тестовый набор в соответствии с соотношением 80% и 20% Здесь мы можем записать 10 наборов данных, 8 из которых используются в качестве обучающего набора и 2 в качестве тестового набора.

второй этап

Вручную маркируйте данные тренировочного набора

Поскольку мы используем алгоритм классификации изображений для определения каждого этапа запуска, мы должны сначала определить этапы запуска, Здесь я разделяю его на этапы 5:

  • 0_desk: Рабочий стол
  • 1_start: этап нажатия на иконку значка
  • 2_splash: этап, на котором появляется страница-заставка.
  • 3_loading: этап загрузки главной страницы
  • 4_stable: стабильная стадия рендеринга домашней страницы

Пять этапов изображены ниже:

Поскольку в приложении также будут рекламные страницы, бизнес-всплывающие окна, динамические изменения на главной странице и т. д., они пока будут игнорироваться и не повлияют на общую идею тестирования.

Извлечение признаков и создание дескрипторов

Здесь выбрана функция SIFT. Функция SIFT не зависит от масштабирования, поворота и освещения и имеет определенную степень устойчивости к геометрическим искажениям и геометрической деформации изображения. Используя интерфейс извлечения функций SIFT в модуле расширения Python OpenCV, он может SIFT характерные точки и дескрипторы изображений.

мешок генерации слов

Генерация пакета слов заключается в создании серии векторных данных на основе данных дескриптора.Наиболее распространенный метод - сначала реализовать кластерный анализ данных дескриптора с помощью K-средних.Как правило, он будет разделен на 100 кластеров и получит каждый По центральным данным класса генерируется сумка из 100 слов.По расстоянию каждого дескриптора до этих кластерных центров определяется, к какому кластеру он принадлежит, так что генерируются данные его гистограммного представления.

Обучение классификации SVM и создание модели

Используйте SVM для обучения классификации данных, чтобы получить выходную модель.Здесь обучение и экспорт модели классификации реализуются с помощью линейного обучения SVM sklearn.

import cv2
import imutils
import numpy as np
import os
from sklearn.svm import LinearSVC
from sklearn.externals import joblib
from scipy.cluster.vq import *
from sklearn.preprocessing import StandardScaler

# Get the training classes names and store them in a list
train_path = "dataset/train/"
training_names = os.listdir(train_path)

# Get all the path to the images and save them in a list
# image_paths and the corresponding label in image_paths
image_paths = []
image_classes = []
class_id = 0
for training_name in training_names:
    dir = os.path.join(train_path, training_name)
    class_path = imutils.imlist(dir)
    image_paths += class_path
    image_classes += [class_id] * len(class_path)
    class_id += 1

# 创建SIFT特征提取器
sift = cv2.xfeatures2d.SIFT_create()

# 特征提取与描述子生成
des_list = []

for image_path in image_paths:
    im = cv2.imread(image_path)
    im = cv2.resize(im, (300, 300))
    kpts = sift.detect(im)
    kpts, des = sift.compute(im, kpts)
    des_list.append((image_path, des))
    print("image file path : ", image_path)

# 描述子向量
descriptors = des_list[0][1]
for image_path, descriptor in des_list[1:]:
    descriptors = np.vstack((descriptors, descriptor))

# 100 聚类 K-Means
k = 100
voc, variance = kmeans(descriptors, k, 1)

# 生成特征直方图
im_features = np.zeros((len(image_paths), k), "float32")
for i in range(len(image_paths)):
    words, distance = vq(des_list[i][1], voc)
    for w in words:
        im_features[i][w] += 1

# 实现动词词频与出现频率统计
nbr_occurences = np.sum((im_features > 0) * 1, axis=0)
idf = np.array(np.log((1.0 * len(image_paths) + 1) / (1.0 * nbr_occurences + 1)), 'float32')

# 尺度化
stdSlr = StandardScaler().fit(im_features)
im_features = stdSlr.transform(im_features)

# Train the Linear SVM
clf = LinearSVC()
clf.fit(im_features, np.array(image_classes))

# Save the SVM
print("training and save model...")
joblib.dump((clf, training_names, stdSlr, k, voc), "startup.pkl", compress=3)

проверка предсказания

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

Вот реализация кода метода прогнозирования:

import cv2 as cv
import numpy as np
from imutils import paths
from scipy.cluster.vq import *
from sklearn.externals import joblib

def predict_image(image_path, pkl):
    # Load the classifier, class names, scaler, number of clusters and vocabulary
    clf, classes_names, stdSlr, k, voc = joblib.load("eleme.pkl")
    # Create feature extraction and keypoint detector objects
    sift = cv.xfeatures2d.SIFT_create()
    # List where all the descriptors are stored
    des_list = []
    im = cv.imread(image_path, cv.IMREAD_GRAYSCALE)
    im = cv.resize(im, (300, 300))
    kpts = sift.detect(im)
    kpts, des = sift.compute(im, kpts)
    des_list.append((image_path, des))

    descriptors = des_list[0][1]
    for image_path, descriptor in des_list[0:]:
        descriptors = np.vstack((descriptors, descriptor))

    test_features = np.zeros((1, k), "float32")
    words, distance = vq(des_list[0][1], voc)
    for w in words:
        test_features[0][w] += 1

    # Perform Tf-Idf vectorization
    nbr_occurences = np.sum((test_features > 0) * 1, axis=0)
    idf = np.array(np.log((1.0 + 1) / (1.0 * nbr_occurences + 1)), 'float32')

    # Scale the features
    test_features = stdSlr.transform(test_features)

    # Perform the predictions
    predictions = [classes_names[i] for i in clf.predict(test_features)]
    return predictions

третий этап

Запишите новое стартовое видео

Так же, как и в Фазе 1.

Предсказать с помощью модели

Сделайте то же самое, что и тестовая модель фазы 2.

Рассчитать время начала

В соответствии с результатами прогнозирования определите изображение на этапе значка щелчка значка приложения и изображение после того, как домашняя страница станет стабильной, и получите прямую разницу номеров кадров между двумя изображениями.Если изображение извлечено с 60 кадрами, тогда общее потребление времени = разница количества кадров * 1/60. Реализация кода этой части конкретного расчета выглядит следующим образом:

from airtest.core.api import *
from dingtalkchatbot.chatbot import DingtalkChatbot
from poco.drivers.android.uiautomation import AndroidUiautomationPoco

webhook = 'https://oapi.dingtalk.com/robot/send?access_token='
robot = DingtalkChatbot(webhook)

def calculate(package_name, apk_path, pkl, device_name, app_name, app_version):
    sample = 'sample/screen.mp4'
    test_path = "dataset/test/"
    if not os.path.isdir('sample/'):
        os.makedirs('sample/')
    if not os.path.isdir(test_path):
        os.makedirs(test_path)
    try:
        os.system("adb uninstall {}".format(package_name))

        os.system("adb install -r {}".format(apk_path))

        poco = AndroidUiautomationPoco()
        poco.device.wake()

        time.sleep(2)

        poco(text='应用名').click()
        poco(text='下一步').click()
        poco(text='允许').click()
        poco(text='允许').click()
        poco(text='允许').click()

        os.system("adb shell am force-stop {}".format(package_name))

        subprocess.Popen("adb shell screenrecord  --time-limit 20 /sdcard/sample.mp4", shell=True,
                         stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

        poco(text="应用名").click()

        time.sleep(20)

        os.system("adb pull /sdcard/sample.mp4 {}".format(sample))
        os.system("adb uninstall {}".format(package_name))

        os.system("ffmpeg -i {} -r 60 {}%d.jpeg".format(sample, test_path))
        image_paths = []
        class_path = list(paths.list_images(test_path))
        image_paths += class_path
        start = []
        stable = []
        for image_path in image_paths:
            predictions = predict_image(image_path, pkl)
            if predictions[0] == '1_start':
                start += [str(image_path.split('/')[2]).split('.')[0]]
            elif predictions[0] == '4_stable':
                stable += [str(image_path.split('/')[2]).split('.')[0]]

        start_time = int(sorted(start)[0])
        stable_time = int(sorted(stable)[0])
        print("耗时:%.2f 秒" % ((stable_time - start_time) / 60))
        robot.send_text(
            msg="启动耗时自动化测试结果:\n被测设备:{}\n被测应用:{}\n被测版本:{}\n".format(device_name, app_name,
                                                                   app_version) + "启动耗时:%.2f 秒" % (
                        (stable_time - start_time) / 60),
            is_at_all=True)
    except:
        shutil.rmtree(test_path)
        if os.path.exists(sample):
            os.remove(sample)


if __name__ == "__main__":
    calculate("package_name", "app/app-release.apk", "startup.pkl", "小米MIX3", "应用名", "10.1.1")

Непрерывная интеграция

В соответствии с параметрами, предоставленными вышеуказанным методом тестирования, настройте задачу через Jenkins, обучите модель, инкапсулируйте три вышеуказанных этапа в виде сценариев Python и настройте WebHook для связи с платформой упаковки для автоматической проверки, анализа и расчета. последний пакет. Загрузка верхней части страницы требует времени.

Эффект

Записав экран вручную, а затем используя QuickTime для просмотра временной шкалы в кадрах, расчетное время загрузки первого экрана в основном находится в пределах 100 миллисекунд от результата, полученного по этой схеме, но этот процесс занимает около 15 минут для выборки данных за один раз. и теперь это решение требует всего около 3 минут для выборки данных за раз, что значительно повышает эффективность и позволяет избежать проблемы несовместимости стандартов работы и сбора разными людьми.