接口函数 ======== 模拟键盘操作 ------------ 可以发送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`` 下的 ``name`` 为 ``hello`` 的值, 可以用以下两种方式: 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`` , 并期望在 ``timeout`` 的 ``10`` 秒钟内, 收到字符串 ``>>>`` 。 若收到, 此语句正常完成, 产品反馈的日志将以字符串形式保存在 ``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`` . 2. 通过设置 ``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_time`` 与 ``cycle_count`` 时, 仅 ``cycle_time`` 生效。 5. 在 ``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" } ] } 若要获取 ``lab`` 中 ``name`` 为 ``hello`` 的值:: 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()`` 可以通过参数 ``col`` 与 ``row`` 改变窗口的大小, 以下是默认值:: 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窗口。 .. image:: ../_static/接口函数/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项目, 可以得到该项目的线性图。 .. image:: ../_static/接口函数/add_measure2.png lib.add_xlsx_measure() ------------------------ ``lib.add_xlsx_measure(sheet, name, value)`` 是 ``lib.add_measure()`` 的封装, 它自动调用项目下的文件 ``demo/measure.xlsx`` 中的 ``low`` 与 ``high`` 值, 然后调用 ``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`` , 所以指标检查通过。