接口函数

模拟键盘操作

可以发送ASCII码模拟键盘操作, 示例代码如下:

uut.send(f"{lib.CTRL_C}", expect=">", timeout=10)
uut.send(f"{lib.ESC}\r", expect=">", timeout=10)

可用的清单如下:

lib.CTRL_A  # 键盘ctrl + 字母A键, 以下组合键意义相同.
lib.CTRL_B
lib.CTRL_C
lib.CTRL_D
lib.CTRL_E
lib.CTRL_F
lib.CTRL_G
lib.CTRL_H
lib.CTRL_I
lib.CTRL_J
lib.CTRL_K
lib.CTRL_L
lib.CTRL_M
lib.CTRL_N
lib.CTRL_O
lib.CTRL_P
lib.CTRL_Q
lib.CTRL_R
lib.CTRL_S
lib.CTRL_T
lib.CTRL_U
lib.CTRL_V
lib.CTRL_W
lib.CTRL_X
lib.CTRL_Y
lib.CTRL_Z
lib.ESC     # 键盘Escape按键, 即ESC键
lib.UP      # 方向键, 向上
lib.DOWN    # 方向键, 向下
lib.RIGHT   # 方向键, 向右
lib.LEFT    # 方向键, 向左

lib.VERSION

获取当前KunLun AutoTest的版本信息, 例如: v23.10.4 .

lib.USERNAME

获取当前测试电脑的登入用户名信息, 例如: lenovo .

lib.HOSTNAME

获取当前测试电脑的名称信息, 例如: desktop1tp185d , 需要说明的是: 这个名称是”修正”后的名称.

lib.PLATFORM

获取当前测试电脑的操作系统类型信息, 它是 WIN 或者是 LINUX

lib.DATA_PATH

获取当前kunlun-data的绝对路径, WIN10下是 C:\kunlun-data , LINUX下是 ~/kunlun-data

lib.SCRIPT_PATH

获取当前开发模式的测试程序所在的绝对路径。

  1. WIN10下是: C:\kunlun-data\kunlun_scripts .

  2. LINUX下是: ~/kunlun-data/kunlun_scripts .

lib.LOG_PATH

获取当前测试的日志所在的绝对路径+当前测试时间戳, 它并不是一个路径, 它的作用是: 用户可以拷贝自定义的文件到日志的路径下, 方便在Test Data页面下载。

例如WIN10下是: C:/kunlun-data/logs/hostname/FT-UUT00/2020/10/01/20201001-120100-FT-UUT00- .

lib.ud

lib.ud 是一个全局字典userdict, 可以在测试代码的任意位置使用。

典型应用场景: 用户定义多个函数, 函数间的变量传递就会变得复杂, 使用 lib.ud 可以轻松解决这个问题。

lib.ud 使用起来也很方便, 如下所示:

  • lib.ud.test_spec = 123 : 设置 test_spec 值为 123

  • spec = lib.ud.test_spec 获取 test_spec 的值, 并保存在变量 spec

需要说明的是:lib.ud 支持任意级层的赋值, 如 lib.ud.test.ft.result.name = "PASS"

lib.pa

返回当前项目下的JSON文件中的值, json的命名必须是 params.json , 如当前项目为 demo , 则JSON文件的路径为 demo/params.json

lib.pa 与接口函数 lib.get_json_params() 的相同点都是获取 params.json 文件中的值, 不同的在于:

  1. lib.pa 使用字典的形式获取, 当JSON定义的参数包含空格时, 会自动使用下划线 _ 代替空格.

  2. lib.get_json_params() 通过函数调用的形式获取, 当入参的值包含空格或是特殊字符时, 推荐用此法.

JSON内容示例如下:

{
    "lab": [
        {
            "description": "it is description2",
            "name": "hello",
            "value": "world"
        }
    ]
}

如果需要获取到 lab 下的 namehello 的值, 可以用以下两种方式:

  1. value = lib.get_json_params("lab", "hello")

  2. value = lib.pa.lab.hello

lib.xlsx

返回当前项目下的XLSX文件中的值, XLSX的命名必须是 measure.xlsx , 如当前项目为 demo , 则XLSX文件的路径为 demo/measure.xlsx .

lib.conn

lib.conn 是一个连接池, 保存着用户在 hostname.py 中为当前 container工位 定义的所有连接。

例如:

  • 用户定义了 add_connection(name="UUT"), 则使用 uut = lib.conn.UUT 可以获取此连接实例。

  • 用户定义了 add_connection(name="REF"), 则使用 uut = lib.conn.REF 可以获取此连接实例。

  • 用户定义了 add_connection(name="COM"), 则使用 uut = lib.conn.COM 可以获取此连接实例。

获取连接实例 uut 后, 可以使用实例的以下几种属性:

  1. uut.open() :打开连接. 需要说的是: 每次 open() 前, 会先调用 close() 方法。

  2. uut.close() :关闭连接

  3. uut.send() : 向连接传递命令行.

  4. uut.buf :保存 uut.send() 语句发送后, 待测产品反馈的日志。

  5. uut.phrase :保存 uut.send() 语句发送后, 收到的 expect 字符串。

  6. uut.capture() :对 uut.buf 进行解析, 获取想要的字符串片段。

需要强调的是:uut.send() 是传递命令行至待测产品UUT的唯一方式, 使用方法如下:

uut.send("python\r", expect=">>>", timeout=10)

对此语句的解释:通过 uut.send 发送命令行 python , 并带回车符 \r , 并期望在 timeout10 秒钟内, 收到字符串 >>> 。 若收到, 此语句正常完成, 产品反馈的日志将以字符串形式保存在 uut.buf 中, 然后执行下一条语句。 若未收到字符串 >>>, 则触发异常(用户可以使用 try...except... 捕获此异常)。

需要说明的是:

  • uut.send()expect 可以接受 列表list, 若 expect列表 时, 当平台收到 列表 中的任意一元素时, uut.send() 语句执行完成, 收到的元素保存在 uut.phrase

    uut.send("dir\r", expect=[">", "#", "$"], timeout=60)
    log.debug(uut.phrase)
    if uut.phrase in [">", "#"]:
        pass
    if uut.phrase in ["$"]:
        raise Exception("Got '$', go to fail")
    
  • uut.send()expect 可以接受 元组tuple, 若 expect元组 时, , 当平台必须收到 元组 中的所有元素, uut.send() 语句执行完成。

    uut.send("dir\r", expect=(">", "TEST PASSED"), timeout=60)
    
  • uut.send() 前会自动清空 uut.buf 内容, uut.send() 完成后, 收到的所有字符会自动保存在 uut.buf 。此例中 uut.send() 完成后, 若使用 log.debug(uut.buf) , 将在事件日志窗口打印出如下字符:

    python
    Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    
  • uut.capture 可以快速截取 uut.buf 中用户想要的字符串, 并支持 正则表达, 其用法如下:

    # 语法说明
    user_string = uut.capture(start_string, end_string="\n")
    # 例如:想要获取Python的版本(此例中, 版本为:3.7.8 )
    version = uut.capture("Python", "(tags")
    

lib.ask_question()

lib.ask_question() 提供一种用户交互方法, 调用此接口函数后, 网页界面将弹出对话框, 用户可以在对话框应答一个问题, 并返回答案(字符串).

典型应用场景:弹出对话框, 要求用户输入产品的序列号:

ans = lib.ask_question("请输入序列号SERIAL: ", timeout=60 * 3)

答案会保存在 ans 中。

需要说明的是:lib.ask_question() 支持传入如下参数:

  1. options : 备选答案, 是一个列表, 例如 ["12345", "678990"]。若传入, 则网页会弹出一个 单项选择题 对话框, 若不设置, 则弹出一个 填空题 对话框。

  2. image : 问题描述图片, 是一个字符串, 例如 "demo\bee.jpg"。若传入, 则弹出的对话框会展示图片信息, 图片高度建议是320px。

  3. visible : 输入内容可视, 默认为 True。若设置为 False, 则用户输入内容显示为 * 号, 常用于密码输入等场景。

  4. multiple : 多选题, 默认为 False。若设置为 True, 则网页弹出一个 多项选择题。当 options 未设置时, multiple 无效。

  5. privilege : 插队, 默认为 False。多 Container工位 提问, 遵循 FIFO先进先出 原则。若设置为 True, 将插队至队列的第一位。

  6. timeout : 超时, 默认为 3600 秒。若在超时时间内, 用户未响应问题, 则触发异常。

  7. tips : 小提示, 默认为 "" , 可以增加一些提示性字符串。

lib.ask_questions()

此接口函数提供一个用户交互方法, 调用此函数后, 网页界面将弹出对话框, 用户可以在对话框同时应答多个问题, 并返回答案(列表).

使用示例:弹出对话框, 要求应答多个问题:

ans = lib.ask_questions(["请输入序列号SERIAL: ", "请输入UUTTYPE: ", "请输入员工工号:"], timeout=60 * 3)

答案将保存在 ans 中。

需要说明的是:lib.ask_questio() 支持传入如下参数:

  1. image : 问题描述图片, 是一个字符串, 例如 "demo\bee.jpg"。若传入, 则弹出的对话框会展示图片信息, 图片高度建议是320px。

  2. privilege : 插队, 默认为 False。多 Container工位 提问, 遵循 FIFO先进先出 原则。若设置为 True, 将插队至队列的第一位。

  3. timeout : 超时, 默认为 3600 秒。若在超时时间内, 用户未响应问题, 则触发异常。

  4. tips : 小提示, 默认为 "" , 可以增加一些提示性字符串。

需要强调的是: lib.ask_questions()lib.ask_question() 共享同一个 FIFO队列

lib.set_display1/2/3/4/5/6()

lib.set_display1/2/3/4/5/6() 可以设置任意信息, 鼠标在container面板上悬停时将展示display1/2/3/4/5/6的内容.

lib.set_step_name()

lib.set_step_name() 设置当前测试项目名称, 若测试项FAIL, 测试记录会自动用此名称作为测试失败项, PASS则不记录。 使用示例:

lib.set_step_name("run cpu test")

lib.get_container_name()

lib.get_container_name() 返回当前的测试工位名称。 使用示例:在 FT:UUT00 上点击开始测试, 返回值为字符串 FT:UUT00

container_name = lib.get_container_name()

lib.add_test_data()

lib.add_test_data() 可以为产品做测试记录。

测试过程中, 使用此接口函数记录产品信息, 测试完成后, 根据测试结果成功或是失败, 自动记录一笔 PASS 或是 FAIL

lib.add_test_data(sernum=serial, uuttype=uuttype, area="FT")

需要强调的是: 此接口函数至少需要传入3个参数, 它们是: sernum 产品条码, uuttype 产品类型, area 测试工序名称。

需要说明的是:此接口函数还支持传入更多参数, 以便帮助用户记录更多信息, 它们是:

  • label1, label2, label3, label4, label5

  • version1, version1, version3, version4, version5

  • str1, str2, str3, str4, str5, str6

lib.get_mode()

lib.get_mode() 返回当前测试的模式, 值为 PRODUCT 或是 DEVELOP

mode = lib.get_mode()

lib.get_params()

lib.get_params() 返回 hostname.py 中当前工站的 add_params_data() 的值.

返回值是一个字典:

params = lib.get_params()

例: 如设置 add_params_data(ip="192.168.1.1", ip=2003), 则返回值为 {"host": "192.168.1.1", "port": 2003}

lib.start_test()

使用一个 container 启动其他 container 测试。

使用示例:

使用 lib.start_test("DEMO:UUT01") 启动 DEMO:UUT01 测试。

使用 lib.start_test(["DEMO:UUT01", "DEMO:UUT02"]) 连续启动多个 container 测试。

lib.stop_test()

使用一个 container 停止其他 container 测试。

使用示例:

使用 lib.stop_test("DEMO:UUT01") 停止 DEMO:UUT01 测试。

使用 lib.stop_test(["DEMO:UUT01", "DEMO:UUT02"]) 连续停止多个 container 测试。

需要说明的是: lib.stop_test() 仅对正在测试中的 container 产生作用.

lib.deposit_test()

使用一个 container 回收其他 container 测试。

使用示例:

使用 lib.deposit_test("DEMO:UUT01") 回收 DEMO:UUT01 测试。

使用 lib.deposit_test(["DEMO:UUT01", "DEMO:UUT02"]) 连续回收多个 container 测试。

需要说明的是: lib.deposit_test() 仅对已经测试完成(PASS/FAIL/STOP)的 container 产生作用.

lib.get_sequence_definition()

返回 sequence 实例, 用此实例编排测试序列, 测试过程中, 可以在 调试界面STEP 窗口查看测试序列流程图。 假设测试代码中定义有如下函数:

def run_test():
    log.debug("welcome to main sequence")
    container_name = lib.get_container_name()
    log.debug(container_name)
    return

def run_test1(name):
    uut = lib.get_uut(protocol="DUMMY")
    log.debug("run test1: dir, name: {}".format(name)
    uut.send("dir\r", expect=">", timeout=10)
    time.sleep(1)
    return

def run_test2():
    uut = lib.conn.UUT
    uut.open()
    log.debug("run test2: cd")
    uut.send("cd\r", expect=">", timeout=10)
    return

def run_test3():
    lib.add_test_data(sernum="1234567890", uuttype="DEMO", area="DEMO")
    log.debug("run test3")
    return

sequence 的基本使用示例:

def main_sequence():
    seq = lib.get_sequence_definition()
    seq.add_step(run_test, name="RUN TEST")
    seq.add_step(run_test1, name="RUN TEST1", kws={"name": "gps test"})
    seq.add_step(run_test2, name="RUN TEST2")
    seq.add_step(run_test3, name="RUN TEST3")
    return seq

注意:add_step() 后要跟函数或方法的名字, 免小括号()

sequence 形式的代码编排, 为测试策略的实施提供了便利, 以下是当前支持的策略, 更多策略持续增加中。

  1. 通过设置 kws={"name": "gps test"} 可以向函数或方法传递参数.

sequence定义传参:

seq.add_step(run_test1, name="RUN TEST1", kws={"name": "gps test"})

需要说明的是: 这里必须使用 kws, 不能使用 kwargs 或是其他关键字.

函数或方法接受传参:

def run_test1(name=""):
    uut = lib.get_uut(protocol="DUMMY")
    log.debug("run test1: dir, name: {}".format(name)
    uut.send("dir\r", expect=">", timeout=10)
    time.sleep(1)
    return

需要说明的是: 函数或方法的入参需要这样定义: xxx=yyy .

  1. 通过设置 in_parallel=True 可以实施 平行测试 策略。

包含 in_parallel 的step或是sequence, 会与下一个step或是sequence平行测试.

使用示例:

def main_sequence():
    seq = lib.get_sequence_definition()
    seq.add_step(run_test, name="RUN TEST")
    seq.add_step(run_test1, name="RUN TEST1", in_parallel=True)
    seq1 = seq.add_sequence("SUB SEQUENCER")
    seq1.add_step(run_test2, name="RUN TEST2")
    seq1.add_step(run_test3, name="RUN TEST3")
    seq.add_step(run_test4, name="RUN TEST4")
    return seq

3. 通过设置 cycle_time=60 * 5 可以实施 按时间循环测试 策略。 例子中 run_test2 每次测试完成后, 会检查累计消耗时间, 若小于 cycle_time , 将再一次测试。 但如果fail, 循环测试终止。

使用示例:

def main_sequence():
    seq = lib.get_sequence_definition()
    seq.add_step(run_test, name="RUN TEST")
    seq.add_step(run_test1, name="RUN TEST1")
    seq.add_step(run_test2, name="RUN TEST2", cycle_time=60 * 5)
    seq.add_step(run_test3, name="RUN TEST3")
    return seq

4. 通过设置 cycle_count=10 可以实施 按次数循环测试 策略。 例子中 run_test2 每次测试完成后, 会检查累计次数, 若小于 cycle_count, 将再执行一次测试, 但如果测试失败, 循环将终止。

使用示例:

def main_sequence():
    seq = lib.get_sequence_definition()
    seq.add_step(run_test, name="RUN TEST")
    seq.add_step(run_test1, name="RUN TEST1")
    seq.add_step(run_test2, name="RUN TEST2", cycle_count=10)
    seq.add_step(run_test3, name="RUN TEST3")
    return seq

需要说明的是: 当在一个 step 上同时使用 cycle_timecycle_count 时, 仅 cycle_time 生效。

  1. get_sequence_definition 中设置 finalize=True , 如果中途测试失败, 程序将自动执行 最后一个step .

使用示例:

def main_sequence():
    seq = lib.get_sequence_definition("SEQ", finalize=True)
    seq.add_step(run_test, name="RUN TEST")
    seq.add_step(run_test1, name="RUN TEST1")
    seq.add_step(run_test2, name="RUN TEST2")
    seq.add_step(run_test3, name="RUN TEST3")
    return seq

本示例中, 如果中途测试失败, 程序将自动执行 run_test3 , 然后再停止测试.

lib.get_json_params()

返回当前项目下的JSON文件中的值, json的命名必须是 params.json , 如当前项目为 demo, 则JSON文件的路径为 demo/params.json

其中, JSON内容示例如下:

{
    "fct": [
        {
            "description": "it is fct description",
            "name": "it is fct name",
            "value": "123"
        }
    ],
    "lab": [
        {
            "description": "it is description",
            "name": "hello",
            "value": "world"
        },
        {
            "description": "it is description2",
            "name": "it is name2",
            "value": "100"
        }
    ]
}

若要获取 labnamehello 的值:

value = lib.get_json_params("lab", "hello")

返回字符串value的值是 world

lib.get_configuration()

hostname.py 中调用此接口函数, 可以更精细化定制用户界面。

使用示例:

from kunlun import lib

def main():
    kl = lib.get_configuration()
    station = kl.add_station("BST", "功能测试", recycle=False, image=r"demo\bee.jpg", dense=False)
    station.add_sequence("project.sequence")
    station.add_params_data(host="192.168.0.1")
    for i in range(4):
        container = station.add_container("UUT{:02d}".format(i))
        container.add_params_data(ip="192.168.1.{}".format(i), test="hello world")
        container.add_connection(name="UUT", protocol="DUMMY")
        container.show_measure_panel()
    station = kl.add_station("BP2", "FT测试", recycle=False)
    station.add_sequence("project.sequence")
    station.add_params_data(host="192.168.0.1")
    for i in range(4):
        container = station.add_container("UUT{:02d}".format(i))
        container.add_connection(name="UUT", protocol="DUMMY")
        container.show_measure_panel()

需要说明的是: show_measure_panel()show_measure_panel() 可以通过参数 colrow 改变窗口的大小, 以下是默认值:

container.show_step_panel(col=12, row=2)
container.show_measure_panel(col=12, row=2)

其中, col 的取值建议是: 4/8/12 , row 的取值建议是 1/2 .

lib.add_measure()

前置条件: 在 hostname.py 中使用 show_measure_panel() , 则Connection页面增加一个MEASURE窗口。

../_images/add_measure1.png

在测试代码中, 使用接口函数, 可以在MEASURE窗口增加一个measure项目, 示例如下:

result = lib.add_measure("TEST_MEASURE1", value=3, low=1, high=4)

或是使用 spec 作为参数:

result = lib.add_measure("TEST_MEASURE1", value=3, spec=[1, 4])

当measure检查通过时 result=True , 否则 result=False .

需要重点说明的是: 参数 value , low , high 或是 spec 的值可以为字符串数字, 接口函数将会将字符串数字自动转为 float 型.

双击measure项目, 可以得到该项目的线性图。

../_images/add_measure2.png

lib.add_xlsx_measure()

lib.add_xlsx_measure(sheet, name, value)lib.add_measure() 的封装, 它自动调用项目下的文件 demo/measure.xlsx 中的 lowhigh 值, 然后调用 lib.add_measure() 进行对比.

在测试代码中, 使用接口函数, 可以在MEASURE窗口增加一个measure项目, 示例如下:

result = lib.add_xlsx_measure("pcba", "meas1", 30)

当measure检查通过时 result=True , 否则 result=False .

demo/measure.xlsx 的格式如下:

name

lower

upper

desc

meas1

20

50

rf

meas2

40

78.9

gps

meas3

-35

35.9

ground

假设XLSX当前的Sheet名为 pcba , 待检查的指标名为 meas1 , 实际测量值为 30 。 调用 lib.add_xlsx_measure("pcba", "meas1", 30), XLSX文件中 meas1 的lower(指标下限)为 20 , upper(指标上限)为 50 , 因为 20<=30<=50 , 所以指标检查通过。