配置 IDT 状态机 - FreeRTOS
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

配置 IDT 状态机

重要

从 IDT v4.5.2 开始,不推荐使用此状态机。强烈建议您使用新的测试编排器。有关更多信息,请参阅配置 IDT 测试编排器

状态机是控制测试套件执行流程的结构。它确定测试套件的起始状态,根据用户定义的规则管理状态转换,并在这些状态之间继续过渡,直到达到结束状态。

如果您的测试套件不包含用户定义的状态机,IDT 将为您生成状态机。默认状态机执行以下功能:

  • 让测试运行者能够选择和运行特定的测试组,而不是整个测试套件。

  • 如果未选择特定的测试组,则按随机顺序运行测试套件中的每个测试组。

  • 生成报告并打印控制台摘要,显示每个测试组和测试用例的测试结果。

IDT 测试套件的状态机必须满足以下条件:

  • 每个状态都对应于 IDT 要采取的操作,例如运行测试组或生成报告文件。

  • 过渡到某个状态会执行与该状态相关的操作。

  • 每个状态定义下一个状态的过渡规则。

  • 最终状态必须为SucceedFail

状态机格式

您可以使用以下模板配置自己的<custom-test-suite-folder>/suite/state_machine.json文件:

{ "Comment": "<description>", "StartAt": "<state-name>", "States": { "<state-name>": { "Type": "<state-type>", // Additional state configuration } // Required states "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } }

包含值的所有字段都为必填字段,如下所述:

Comment

状态机的描述。

StartAt

IDT 开始运行测试套件的状态名称。的值StartAt必须设置为States对象中列出的状态之一。

States

将用户定义的状态名称映射到有效的 IDT 状态的对象。每个国家。 state-nam e 对象包含映射到状态名的有效状态的定义。

States对象必须包含SucceedFail状态。有关有效状态的信息,请参阅有效的状态和状态定义

有效的状态和状态定义

本节介绍可在 IDT 状态机中使用的所有有效状态的状态定义。以下某些状态支持测试用例级别的配置。但是,除非绝对必要,否则我们建议您在测试组级别而不是测试用例级别配置状态转换规则。

RunTask

RunTask州运行来自测试套件中定义的测试组的测试用例。

{ "Type": "RunTask", "Next": "<state-name>", "TestGroup": "<group-id>", "TestCases": [ "<test-id>" ], "ResultVar": "<result-name>" }

包含值的所有字段都为必填字段,如下所述:

Next

在当前状态下执行操作后要过渡到的状态的名称。

TestGroup

可选。要运行的测试组的 ID。如果未指定此值,则 IDT 将运行测试运行者选择的测试组。

TestCases

可选。来自中指定组的测试用例 ID 数组TestGroup。根据TestGroup和的值TestCases,IDT 确定测试执行行为,如下所示:

  • 当同时指定TestGroupTestCases时,IDT 会运行测试组中的指定测试用例。

  • 如果TestCases已指定但TestGroup未指定,IDT 将运行指定的测试用例。

  • 指定但TestCases未指定时TestGroup,IDT 将运行指定测试组中的所有测试用例。

  • 如果未指定TestGroupTestCases,IDT 将运行测试运行器从 IDT CLI 中选择的测试组中的所有测试用例。要为测试运行者启用分组选择,必须在statemachine.json文件中同时包含RunTaskChoice状态。有关其工作原理的示例,请参阅状态机示例:运行用户选择的测试组

    有关为测试运行器启用 IDT CLI 命令的更多信息,请参阅启用 IDT CLI 命令

ResultVar

要使用测试运行结果设置的上下文变量的名称。如果您没有为指定值,请不要指定此值TestGroup。IDT 将您在其中定义的变量的值设置ResultVartruefalse基于以下内容:

  • 如果变量名称的格式为text_text_passed,则该值将设置为第一个测试组中的所有测试都通过还是被跳过。

  • 在所有其他情况下,该值设置为所有测试组中的所有测试都通过还是被跳过。

通常,您将使用RunTask状态来指定测试组 ID,而不指定单个测试用例 ID,因此 IDT 将运行指定测试组中的所有测试用例。在此状态下运行的所有测试用例均按随机顺序parallel 运行。但是,如果所有测试用例都需要一台设备运行,并且只有一台设备可用,则测试用例将改为按顺序运行。

错误处理

如果任何指定的测试组或测试用例 ID 无效,则此状态会发出RunTaskError执行错误。如果状态遇到执行错误,则它还会将状态机上下文中的hasExecutionError变量设置为true

Choice

Choice状态允许您根据用户定义的条件动态设置要过渡的下一个状态。

{ "Type": "Choice", "Default": "<state-name>", "FallthroughOnError": true | false, "Choices": [ { "Expression": "<expression>", "Next": "<state-name>" } ] }

包含值的所有字段都为必填字段,如下所述:

Default

如果无法对中Choices定义的表达式求值,则过渡到的默认状态true

FallthroughOnError

可选。指定状态在计算表达式时遇到错误时的行为。true如果要在求值导致错误时跳过表达式,则设置为。如果没有匹配的表达式,则状态机将过渡到Default状态。如果未指定该FallthroughOnError值,则默认为false

Choices

一系列表达式和状态,用于确定在当前状态下执行操作后要过渡到哪个状态。

Choices.Expression

计算结果为布尔值的表达式字符串。如果表达式的计算结果为true,则状态机将转换为中定义的状态Choices.Next。表达式字符串从状态机上下文中检索值,然后对它们执行操作以得出布尔值。有关访问状态机上下文的信息,请参见状态机上下文

Choices.Next

如果中定义的表达式的Choices.Expression计算结果为,则为要过渡到的状态的名称true

错误处理

在以下情况下,Choice状态可能需要错误处理:

  • 选择表达式中的某些变量在状态机上下文中不存在。

  • 表达式的结果不是布尔值。

  • JSON 查询的结果不是字符串、数字或布尔值。

在此状态下,您不能使用Catch块来处理错误。如果要在状态机遇到错误时停止执行该状态机,则必须将设置FallthroughOnErrorfalse。但是,建议您设置为trueFallthroughOnError并根据您的用例执行以下操作之一来使用:

  • 如果您正在访问的变量在某些情况下预计不存在,则使用的值Default和其他Choices块来指定下一个状态。

  • 如果您正在访问的变量应始终存在,则将Default状态设置为Fail

Parallel

Parallel状态允许你定义和运行新的状态机parallel 运行。

{ "Type": "Parallel", "Next": "<state-name>", "Branches": [ <state-machine-definition> ] }

包含值的所有字段都为必填字段,如下所述:

Next

在当前状态下执行操作后要过渡到的状态的名称。

Branches

要运行的状态机定义数组。每个状态机定义都必须包含自己的StartAtSucceed、和Fail状态。此数组中的状态机定义不能引用其自身定义之外的状态。

注意

由于每个分支状态机共享相同的状态机上下文,因此在一个分支中设置变量然后从另一个分支读取这些变量可能会导致意外行为。

只有在Parallel运行所有分支状态机之后,状态才会移动到下一个状态。每个需要设备的状态都将等待运行,直到设备可用为止。如果有多个设备可用,则此状态会parallel 运行来自多个组的测试用例。如果没有足够的设备可用,则测试用例将按顺序运行。由于测试用例在parallel 运行时是按随机顺序运行的,因此可能会使用不同的设备来运行来自同一测试组的测试。

错误处理

确保分支状态机和父状态机都转换为处理执行错误的Fail状态。

由于分支状态机不会将执行错误传输到父状态机,因此您无法使用Catch块来处理分支状态机中的执行错误。改为在共享状态机上下文中使用该hasExecutionErrors值。有关其工作原理的示例,请参阅状态机示例:parallel 运行两个测试组

AddProductFeatures

AddProductFeatures状态允许您将产品功能添加到 IDT 生成的awsiotdevicetester_report.xml文件中。

产品功能是用户定义的有关设备可能满足的特定标准的信息。例如,MQTT产品功能可以指定设备正确发布 MQTT 消息。在报告中,根据指定的测试是否通过supportednot-supported,将产品功能设置为、或自定义值。

注意

AddProductFeatures州本身不生成报告。此状态必须过渡到该Report状态才能生成报告。

{ "Type": "Parallel", "Next": "<state-name>", "Features": [ { "Feature": "<feature-name>", "Groups": [ "<group-id>" ], "OneOfGroups": [ "<group-id>" ], "TestCases": [ "<test-id>" ], "IsRequired": true | false, "ExecutionMethods": [ "<execution-method>" ] } ] }

包含值的所有字段都为必填字段,如下所述:

Next

在当前状态下执行操作后要过渡到的状态的名称。

Features

要在awsiotdevicetester_report.xml文件中显示的一系列产品功能。

Feature

功能的名称

FeatureValue

可选。要在报告中使用的自定义值,而不是supported。如果未指定此值,则根据测试结果,将特征值设置为supportednot-supported

如果您使用自定义值FeatureValue,则可以在不同的条件下测试同一个功能,IDT 会将支持条件的功能值连接起来。例如,以下摘录显示了具有两个单独MyFeature功能值的功能:

... { "Feature": "MyFeature", "FeatureValue": "first-feature-supported", "Groups": ["first-feature-group"] }, { "Feature": "MyFeature", "FeatureValue": "second-feature-supported", "Groups": ["second-feature-group"] }, ...

如果两个测试组都通过,则功能值设置为first-feature-supported, second-feature-supported

Groups

可选。测试组 ID 的数组。每个指定测试组内的所有测试都必须通过才能支持该功能。

OneOfGroups

可选。测试组 ID 的数组。必须通过至少一个指定测试组内的所有测试才能支持该功能。

TestCases

可选。测试用例 ID 的数组。如果您指定此值,则以下内容适用:

  • 必须通过所有指定的测试用例才能支持该功能。

  • Groups必须仅包含一个测试组 ID。

  • OneOfGroups不得指定。

IsRequired

可选。设置为false,在报告中将此功能标记为可选功能。默认值为 true

ExecutionMethods

可选。与device.json文件中指定protocol值相匹配的执行方法数组。如果指定了此值,则测试运行器必须指定一个与该数组中的一个protocol值相匹配的值,才能将该功能包含在报告中。如果未指定此值,则该功能将始终包含在报告中。

要使用AddProductFeatures状态,必须将RunTask状态ResultVar中的值设置为以下值之一:

  • 如果您指定了单个测试用例 ID,则设置ResultVargroup-id_test-id_passed

  • 如果您未指定单个测试用例 ID,则设置ResultVargroup-id_passed

AddProductFeatures州通过以下方式检查测试结果:

  • 如果您未指定任何测试用例 ID,则每个测试组的结果将根据状态机上下文中group-id_passed变量的值确定。

  • 如果您确实指定了测试用例 ID,则每个测试的结果都是根据状态机上下文中group-id_test-id_passed变量的值确定的。

错误处理

如果在此状态下提供的群组 ID 不是有效的群组 ID,则此状态会导致AddProductFeaturesError执行错误。如果状态遇到执行错误,则它还会将状态机上下文中的hasExecutionErrors变量设置为true

报告

Report状态生成suite-name_Report.xmlawsiotdevicetester_report.xml文件。此状态还会将报告流式传输到控制台。

{ "Type": "Report", "Next": "<state-name>" }

包含值的所有字段都为必填字段,如下所述:

Next

在当前状态下执行操作后要过渡到的状态的名称。

您应始终过渡到测试执行流程快要结束时的Report状态,以便测试运行者可以查看测试结果。通常,此状态之后的下一个状态是Succeed

错误处理

如果此状态在生成报告时遇到问题,则会发出ReportError执行错误。

LogMessage

LogMessage状态生成test_manager.log文件并将日志消息流式传输到控制台。

{ "Type": "LogMessage", "Next": "<state-name>" "Level": "info | warn | error" "Message": "<message>" }

包含值的所有字段都为必填字段,如下所述:

Next

在当前状态下执行操作后要过渡到的状态的名称。

Level

创建日志消息的错误级别。如果您指定的级别无效,则此状态会生成一条错误消息并将其丢弃。

Message

要记录的消息。

SelectGroup

SelectGroup状态会更新状态机上下文以指示选择了哪些组。此状态设置的值由任何后续Choice状态使用。

{ "Type": "SelectGroup", "Next": "<state-name>" "TestGroups": [ <group-id>" ] }

包含值的所有字段都为必填字段,如下所述:

Next

在当前状态下执行操作后要过渡到的状态的名称。

TestGroups

一组将被标记为已选中的测试组。对于此数组中的每个测试组 ID,该group-id_selected变量在上下文true中设置为。请确保提供有效的测试组 ID,因为 IDT 不会验证指定的组是否存在。

Fail

Fail状态表明状态机未正确执行。这是状态机的结束状态,每个状态机定义都必须包含此状态。

{ "Type": "Fail" }

Succeed

Succeed状态表明状态机已正确执行。这是状态机的结束状态,每个状态机定义都必须包含此状态。

{ "Type": "Succeed" }

状态机上下文

状态机上下文是一个只读 JSON 文档,其中包含状态机在执行期间可用的数据。状态机上下文只能从状态机访问,并且包含决定测试流程的信息。例如,您可以使用测试运行器在userdata.json文件中配置的信息来确定是否需要运行特定的测试。

状态机上下文使用以下格式:

{ "pool": { <device-json-pool-element> }, "userData": { <userdata-json-content> }, "config": { <config-json-content> }, "suiteFailed": true | false, "specificTestGroups": [ "<group-id>" ], "specificTestCases": [ "<test-id>" ], "hasExecutionErrors": true }
pool

有关为测试运行选择的设备池的信息。对于选定的设备池,此信息是从device.json文件中定义的相应顶级设备池数组元素中检索的。

userData

userdata.json文件中的信息。

config

信息固定在config.json文件中。

suiteFailed

该值在状态机启动false时设置为。如果测试组在某个RunTask状态下失败,则在状态机执行true的剩余时间内,此值将设置为。

specificTestGroups

如果测试运行器选择运行特定的测试组而不是整个测试套件,则会创建此密钥并包含特定测试组 ID 的列表。

specificTestCases

如果测试运行器选择要运行的特定测试用例而不是整个测试套件,则会创建此密钥并包含特定测试用例 ID 的列表。

hasExecutionErrors

状态机启动时不退出。如果任何状态遇到执行错误,则在状态机执行的剩余true时间内创建此变量并将其设置为。

你可以使用 jsonPath 表示法查询上下文。状态定义中 jsonPath 查询的语法是{{$.query}}。在某些状态下,您可以将 JSONPath 查询用作占位符字符串。IDT 将占位符字符串替换为上下文中评估的 jsonPath 查询的值。可以对以下值使用占位符:

  • RunTask状态TestCases表示的值。

  • ExpressionChoice状态。

当您访问状态机上下文中的数据时,请确保满足以下条件:

  • 你的 JSON 路径必须以$.

  • 每个值的计算结果必须为字符串、数字或布尔值。

有关使用 JSONPath 表示法从上下文访问数据的更多信息,请参阅使用 IDT 上下文

执行错误

执行错误是状态机在执行状态时遇到的状态机定义错误。IDT 在test_manager.log文件中记录有关每个错误的信息,并将日志消息流式传输到控制台。

您可以使用以下方法处理执行错误:

抓住

要使用Catch,请将以下内容添加到您的州定义中:

"Catch": [ { "ErrorEquals": [ "<error-type>" ] "Next": "<state-name>" } ]

包含值的所有字段都为必填字段,如下所述:

Catch.ErrorEquals

要catch 的错误类型的数组。如果执行错误与其中一个指定值匹配,则状态机将转换为中指定的状态Catch.Next。有关其产生的错误类型的信息,请参阅每个状态的定义。

Catch.Next

如果当前状态遇到与中指定的值之一匹配的执行错误,则过渡到的下一个状态Catch.ErrorEquals

捕获区块是按顺序处理的,直到一个匹配为止。如果没有错误与 Catch 块中列出的错误相匹配,则状态机将继续执行。由于执行错误是状态定义不正确的结果,因此我们建议您在状态遇到执行错误时转换到 Fail 状态。

hasExecutionError

当某些状态遇到执行错误时,除了发出错误外,它们还会在状态机上下文true中将hasExecutionError值设置为。您可以使用此值来检测何时发生错误,然后使用Choice状态将状态机转换为Fail状态。

该方法具有以下特性。

  • 状态机在启动时不会为其分配任何值hasExecutionError,并且在特定状态设置该值之前,该值不可用。这意味着您必须false为访问此值的FallthroughOnErrorChoice状态明确设置为,以防止状态机在未发生执行错误时停止。

  • 一旦将其设置为truehasExecutionError就永远不会设置为 false 或从上下文中删除。这意味着该值仅在首次设置为时有用true,并且对于所有后续状态,它不提供有意义的值。

  • hasExecutionError值与处于该Parallel状态的所有分支状态机共享,这可能会导致意外结果,具体取决于访问该值的顺序。

由于这些特性,如果可以改用 Catch 块,我们不建议您使用此方法。

示例状态机

本节提供一些状态机配置示例。

状态机示例:运行单个测试组

这个状态机:

  • 使用 ID 运行测试组GroupA,该组必须存在于套件中的group.json文件中。

  • 检查是否存在执行错误,Fail如果发现任何错误,则转换为。

  • 生成报告,Succeed如果没有错误,则转换为,Fail否则转换为。

{ "Comment": "Runs a single group and then generates a report.", "StartAt": "RunGroupA", "States": { "RunGroupA": { "Type": "RunTask", "Next": "Report", "TestGroup": "GroupA", "Catch": [ { "ErrorEquals": [ "RunTaskError" ], "Next": "Fail" } ] }, "Report": { "Type": "Report", "Next": "Succeed", "Catch": [ { "ErrorEquals": [ "ReportError" ], "Next": "Fail" } ] }, "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } }

状态机示例:运行用户选择的测试组

这个状态机:

  • 检查测试运行者是否选择了特定的测试组。状态机不会检查特定的测试用例,因为如果不选择测试组,测试运行器就无法选择测试用例。

  • 如果选择了测试组:

    • 在选定的测试组中运行测试用例。为此,状态机没有明确指定RunTask状态中的任何测试组或测试用例。

    • 运行所有测试后生成报告并退出。

  • 如果未选择测试组:

    • 在测试组中运行测试GroupA

    • 生成报告并退出。

{ "Comment": "Runs specific groups if the test runner chose to do that, otherwise runs GroupA.", "StartAt": "SpecificGroupsCheck", "States": { "SpecificGroupsCheck": { "Type": "Choice", "Default": "RunGroupA", "FallthroughOnError": true, "Choices": [ { "Expression": "{{$.specificTestGroups[0]}} != ''", "Next": "RunSpecificGroups" } ] }, "RunSpecificGroups": { "Type": "RunTask", "Next": "Report", "Catch": [ { "ErrorEquals": [ "RunTaskError" ], "Next": "Fail" } ] }, "RunGroupA": { "Type": "RunTask", "Next": "Report", "TestGroup": "GroupA", "Catch": [ { "ErrorEquals": [ "RunTaskError" ], "Next": "Fail" } ] }, "Report": { "Type": "Report", "Next": "Succeed", "Catch": [ { "ErrorEquals": [ "ReportError" ], "Next": "Fail" } ] }, "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } }

状态机示例:运行包含产品功能的单个测试组

这个状态机:

  • 运行测试组GroupA

  • 检查是否存在执行错误,Fail如果发现任何错误,则转换为。

  • 将该FeatureThatDependsOnGroupA功能添加到awsiotdevicetester_report.xml文件中:

    • 如果GroupA通过,则该功能设置为supported

    • 该功能在报告中未标记为可选。

  • 生成报告,Succeed如果没有错误,则转换为,Fail否则

{ "Comment": "Runs GroupA and adds product features based on GroupA", "StartAt": "RunGroupA", "States": { "RunGroupA": { "Type": "RunTask", "Next": "AddProductFeatures", "TestGroup": "GroupA", "ResultVar": "GroupA_passed", "Catch": [ { "ErrorEquals": [ "RunTaskError" ], "Next": "Fail" } ] }, "AddProductFeatures": { "Type": "AddProductFeatures", "Next": "Report", "Features": [ { "Feature": "FeatureThatDependsOnGroupA", "Groups": [ "GroupA" ], "IsRequired": true } ] }, "Report": { "Type": "Report", "Next": "Succeed", "Catch": [ { "ErrorEquals": [ "ReportError" ], "Next": "Fail" } ] }, "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } }

状态机示例:parallel 运行两个测试组

这个状态机:

  • GroupA并parallel 运行和GroupB测试组。分支状态机中的RunTask状态存储在上下文中的ResultVar变量可供AddProductFeatures状态使用。

  • 检查是否存在执行错误,Fail如果发现任何错误,则转换为。此状态机不使用Catch块,因为该方法无法检测分支状态机中的执行错误。

  • 根据通过的群组向awsiotdevicetester_report.xml文件添加功能

    • 如果GroupA通过,则该功能设置为supported

    • 该功能在报告中未标记为可选。

  • 生成报告,Succeed如果没有错误,则转换为,Fail否则

如果在设备池中配置了两台设备,则两台设备GroupAGroupB可以同时运行。但是,如果其中GroupB一个GroupA或包含多个测试,则可以将两个设备分配给这些测试。如果仅配置一台设备,则测试组将按顺序运行。

{ "Comment": "Runs GroupA and GroupB in parallel", "StartAt": "RunGroupAAndB", "States": { "RunGroupAAndB": { "Type": "Parallel", "Next": "CheckForErrors", "Branches": [ { "Comment": "Run GroupA state machine", "StartAt": "RunGroupA", "States": { "RunGroupA": { "Type": "RunTask", "Next": "Succeed", "TestGroup": "GroupA", "ResultVar": "GroupA_passed", "Catch": [ { "ErrorEquals": [ "RunTaskError" ], "Next": "Fail" } ] }, "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } }, { "Comment": "Run GroupB state machine", "StartAt": "RunGroupB", "States": { "RunGroupA": { "Type": "RunTask", "Next": "Succeed", "TestGroup": "GroupB", "ResultVar": "GroupB_passed", "Catch": [ { "ErrorEquals": [ "RunTaskError" ], "Next": "Fail" } ] }, "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } } ] }, "CheckForErrors": { "Type": "Choice", "Default": "AddProductFeatures", "FallthroughOnError": true, "Choices": [ { "Expression": "{{$.hasExecutionErrors}} == true", "Next": "Fail" } ] }, "AddProductFeatures": { "Type": "AddProductFeatures", "Next": "Report", "Features": [ { "Feature": "FeatureThatDependsOnGroupA", "Groups": [ "GroupA" ], "IsRequired": true }, { "Feature": "FeatureThatDependsOnGroupB", "Groups": [ "GroupB" ], "IsRequired": true } ] }, "Report": { "Type": "Report", "Next": "Succeed", "Catch": [ { "ErrorEquals": [ "ReportError" ], "Next": "Fail" } ] }, "Succeed": { "Type": "Succeed" }, "Fail": { "Type": "Fail" } } }