Monkey / automatic traversal test to integrate screen recording requirements

Posted by neorunner on Mon, 14 Feb 2022 12:29:11 +0100

1. Demand

Recent development reaction: Monkey / Auto traverses the FC, ANR and other problems during the test. It is impossible to accurately locate the cause and recurrence path of the problem through the log. It is hoped that the recording screen can be added during the test to facilitate the location of the path and cause of the problem recurrence. (for automatic traversal test, please refer to: https://github.com/bytedance/Fastbot_Android)
Defects of mobile phone's own recording screen:

  1. The duration cannot be controlled during the test
  2. It is easy to be interrupted in the test
  3. Long time screen recording consumes too much mobile phone memory

2. Solution ideas

Monkey / when traversing the test automatically, record the screen intermittently through the adb command, and then analyze the test log. If there is no error message, delete the previous record screen; otherwise, retain and export the record screen.

3. Flow chart

flow chart. jpg

4. Test process

4.1. Third party libraries required for Python environment and source code

4.2. Modify monkey as required_ through. Yaml configuration information (note the space between fields)

4.3. Turn on the log provided by the device and keep usb connected to the device. Do not interrupt during the test

4.4. Execute the main() method (in case of abnormality, the test will be automatically terminated and the screen recording will be exported to the script directory)

5. Yaml profile example

# Test type: Monkey/Through
testType: Through
# [Monkey]
Monkey:
  # Package name
  packageName: com.google.android.dialer
  # Number of runs
  runTimes: 500
  # Seed value
  runSeed: 999
  # Event flow interval in milliseconds
  runThrottle: 500
  pctTouch: 50
  pctMotion: 20
  pctTrackball: 5
  pctSyskeys: 5
  pctAppSwitch: 5
  pctRotation: 5
# [automatic traversal]
Through:
  # Package name
  packageName: com.example.anrfcapp
  # Test clock (unit: minutes)
  runMinutes: 10
  # Time flow interval
  runThrottle: 500
# Screen recording configuration
screenRecord:
  # Recording duration (in seconds)
  recordTime: 60

5. Source code

#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time    : 2021/11/15 16:31
# @Author  : CuiShuangqi
# @Email   : 1159533975@qq.com
# @File    : monkeyScreenRecord.py

"""
    Requirements: Integration Monkey,Automatically traverse the test and add the screen recording function(Intermittent screen recording. If there is no error, the previous screen recording will be deleted)
    Note: please modify before testing monkey_through.yaml configuration file
"""
import os
import time
import yaml


# Get mobile information
def get_device_info():
    # Waiting for phone connection
    print("Waiting for device connection...")
    os.system("adb wait-for-device")
    print("Device connected successfully,The parameters are as follows:")
    # Mobile phone model
    phoneModel = os.popen("adb shell getprop ro.product.model").read().strip('\n')
    # Mobile SOC
    socModel = os.popen("adb shell getprop ro.board.platform").read().strip('\n').upper()
    # Software version
    softwareVersion = os.popen("adb shell getprop ro.build.display.id").read().strip('\n')
    # Android version
    androidVersion = os.popen("adb shell getprop ro.build.version.release").read().strip('\n')
    print(f"[[mobile phone model]:{phoneModel}",
          f"\n[[mobile platform]:{socModel}",
          f"\n[[software version]:{softwareVersion}",
          f"\n[Android version]:{androidVersion}")


# Read configuration information
def read_yaml(test_type: str):
    yamlPath = os.path.join(os.getcwd(), "monkey_through.yaml")
    with open(yamlPath, "r", encoding="GBK") as f:
        return yaml.load(f.read(), Loader=yaml.FullLoader)[test_type]


# Judge the mobile phone test environment
def is_test_environment(test_type: str) -> bool:
    # Current directory of script
    curPath = os.getcwd()
    flag = False
    # Monkey test
    if test_type == "Monkey":
        print("Monkey There is no need to configure the test environment for testing!")
        flag = True
    # Automatic traversal test
    elif test_type == "Through":
        jarFiles = os.popen("adb shell find /sdcard/ -name '*.jar'").read()
        if "/sdcard/framework.jar" in jarFiles and "/sdcard/monkeyq.jar" in jarFiles:
            print("Already exists in the device framework.jar,monkeyq.jar!")
            flag = True
        else:
            cur_files = []
            for root, dirs, files in os.walk(os.getcwd()):
                for f in files:
                    cur_files.append(os.path.join(root, f))
            frameworkJarPath = os.path.join(curPath, "framework.jar")
            monkeyqJarPath = os.path.join(curPath, "monkeyq.jar")
            # Judge whether there are jar packages in the script directory
            if frameworkJarPath in cur_files and monkeyqJarPath in cur_files:
                # There are jar packages in the current directory. Push the jar package to / sdcard/
                if os.system(f"adb push {frameworkJarPath} /sdcard/") == 0 and \
                        os.system(f"adb push {monkeyqJarPath} /sdcard/") == 0:
                    print("Push framework.jar,monkeyq.jar to/sdcard/Directory succeeded!")
                    flag = True
                else:
                    print("Push framework.jar,monkeyq.jar to/sdcard/Directory failed!")
                    flag = False
            else:
                print("The current directory of the script does not exist framework.jar,monkeyq.jar!")
                flag = False
    else:
        print("Test type parameter error!")
        flag = False
    return flag


def test_monkey_or_through(test_type: str):
    # Background operation
    if test_type == "Monkey":
        monkeyCommand = f'adb shell "monkey ' \
                        f'-p {read_yaml("Monkey")["packageName"]} ' \
                        f'--throttle {read_yaml("Monkey")["runThrottle"]} ' \
                        f'--randomize-throttle ' \
                        f'--ignore-crashes ' \
                        f'--ignore-timeouts ' \
                        f'--ignore-security-exceptions ' \
                        f'--ignore-native-crashes ' \
                        f'--pct-touch {read_yaml("Monkey")["pctTouch"]} ' \
                        f'--pct-motion {read_yaml("Monkey")["pctMotion"]} ' \
                        f'--pct-trackball {read_yaml("Monkey")["pctTrackball"]} ' \
                        f'--pct-syskeys {read_yaml("Monkey")["pctSyskeys"]} ' \
                        f'--pct-appswitch {read_yaml("Monkey")["pctAppSwitch"]} ' \
                        f'--pct-rotation {read_yaml("Monkey")["pctRotation"]} ' \
                        f'-v -v -v {read_yaml("Monkey")["runTimes"]} ' \
                        f'1>/sdcard/monkeyInfo.txt 2>/sdcard/monkeyError.txt &"'
        os.system(monkeyCommand)
    elif test_type == "Through":
        throughCommand = f'adb shell ' \
                         f'"CLASSPATH=/sdcard/monkeyq.jar:/sdcard/framework.jar ' \
                         f'exec app_process /system/bin com.android.commands.monkey.Monkey ' \
                         f'-p {read_yaml("Through")["packageName"]} ' \
                         f'--agent robot ' \
                         f'--running-minutes {read_yaml("Through")["runMinutes"]} ' \
                         f'--throttle {read_yaml("Through")["runThrottle"]} -v -v -v ' \
                         f'1>/sdcard/throughInfo.txt 2>/sdcard/throughError.txt &"'
        os.system(throughCommand)


# Adobe Captivate 
def screen_record(test_type: str) -> str:
    # Recording seconds
    recordTime = read_yaml("screenRecord")["recordTime"]
    # Recording file name
    timeFlag = time.strftime('%Y-%m-%d_%H%M%S', time.localtime(time.time()))
    recordName = f"/sdcard/{test_type}_{timeFlag}.mp4"
    print("Recording screen...Do not disconnect!")
    recordCommand = f'adb shell screenrecord --time-limit {recordTime} {recordName}'
    os.system(recordCommand)
    print("Recording screen has ended!")
    # Returns the file name of the recorded mp4, so that the file can be deleted when there is no exception
    return recordName


# Judge whether the mobile phone is abnormal
def is_Exception(testType: str) -> bool:
    flag = False
    if testType == "Monkey":
        # Parse / sdcard / monkeyerror txt
        throughErrorInfo = os.popen('adb shell cat /sdcard/monkeyError.txt').read()
        if "ANR" in throughErrorInfo or "NOT RESPONDING" in throughErrorInfo or "CRASH" in throughErrorInfo:
            flag = True
            print("Monkey There are exceptions in the test!!!")
    elif testType == "Through":
        # Parse / sdcard / througherror txt
        throughErrorInfo = os.popen('adb shell cat /sdcard/throughError.txt').read()
        if "ANR" in throughErrorInfo or "NOT RESPONDING" in throughErrorInfo or "CRASH" in throughErrorInfo:
            flag = True
            print("There is an exception in automatic traversal!!!")
    # Export video

    else:
        print("No abnormality found in this test!")
    return flag


# main
def main():
    get_device_info()
    testType = read_yaml("testType")
    if is_test_environment(testType):
        # Start testing
        test_monkey_or_through(testType)
        while True:
            # Recording screen
            recordName = screen_record(testType)
            findMonkeyPsCommand = 'adb shell ps | findstr -i "monkey"'
            commandList = os.popen(findMonkeyPsCommand).read().strip("\n").split(" ")
            if commandList[0] != "":
                # Judge whether there is any abnormality
                if is_Exception(testType):
                    # Export screen recording to current directory
                    if os.system(f"adb pull {recordName} {os.getcwd()}") == 0:
                        print(f"Video exported to:[{os.getcwd()}]")
                    else:
                        print(f"Failed to export video, saved:{recordName}")
                    # Query the result of monkey process id and empty it
                    myList = [i for i in commandList if i != '']
                    # The second value in the list is the monkey process id
                    monkeyId = myList[1]
                    # kill the traversal process
                    if os.system(f"adb shell kill {monkeyId}") == 0:
                        print("Process ended successfully!")
                    else:
                        print("Process end failed,Please try to end the process manually!")
                    break
                else:
                    # Delete the previous screen recording and record it again
                    if os.system(f"adb shell rm {recordName}") == 0:
                        print("Screen recording deleted successfully!")
                    else:
                        print("Failed to delete screen recording!")
                        break
            else:
                # The test has ended
                print("The test has ended,No abnormality found, the last screen recording of the test has been saved!")
                break
    else:
        print("Failed to initialize test environment!")


if __name__ == '__main__':
    main()

The following is the supporting materials. For friends who do [software testing], it should be the most comprehensive and complete war preparation warehouse. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you!

Finally, it can be in the official account: programmer Hao! Get a 216 page interview document of Software Test Engineer for free. And the corresponding video learning tutorials for free!, It includes basic knowledge, Linux essentials, Shell, Internet program principles, Mysql database, special topics of packet capture tools, interface test tools, test advanced Python programming, Web automation test, APP automation test, interface automation test, advanced continuous integration of test, test architecture, development test framework, performance test, security test, etc.

If my blog is helpful to you and you like my blog content, please click "like", "comment" and "collect" for three times! Friends who like software testing can join our testing technology exchange group: 779450660 (there are various software testing resources and technical discussions)

Topics: Java Programmer software testing