AWS IoT Greengrass Version 1 2023 年 6 月 30 日进入延长寿命阶段。有关更多信息,请参阅 AWS IoT Greengrass V1 维护策略。在此日期之后,将 AWS IoT Greengrass V1 不会发布提供功能、增强功能、错误修复或安全补丁的更新。在上面运行的设备 AWS IoT Greengrass V1 不会中断,将继续运行并连接到云端。我们强烈建议您迁移到 AWS IoT Greengrass Version 2,这样可以添加重要的新功能并支持其他平台。
本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
配置 IDT 状态机
状态机是一种控制测试套件执行流程的构造。它决定测试套件的起始状态,根据用户定义的规则管理状态转换,并继续在这些状态之间进行转换,直到达到结束状态。
如果您的测试套件不包含用户定义的状态机,IDT 将为您生成状态机。默认状态机执行以下功能:
-
使测试运行者能够选择和运行特定的测试组,而不是整个测试套件。
-
如果未选择特定的测试组,则按随机顺序运行测试套件中的每个测试组。
-
生成报告并打印控制台摘要,其中显示每个测试组和测试用例的测试结果。
IDT 测试套件的状态机必须满足以下条件:
-
每个状态都对应于 IDT 要采取的操作,例如运行测试组或产品报告文件。
-
过渡到状态会执行与该状态关联的操作。
-
每个状态都定义了下一个状态的过渡规则。
-
结束状态必须为
Succeed
或Fail
。
状态机格式
您可以使用以下模板来配置自己的
文件:<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-name
对象包含映射到state-name
有效状态的定义。States
对象必须包含Succeed
和Fail
状态。有关有效状态的信息,请参阅有效状态和状态定义。
有效状态和状态定义
本节介绍可在 IDT 状态机中使用的所有有效状态的状态定义。以下某些状态支持测试用例级别的配置。但是,除非绝对必要,否则我们建议您在测试组级别而不是测试用例级别配置状态转换规则。
RunTask
RunTask
状态运行测试套件中定义的测试组中的测试用例。
{ "Type": "RunTask", "Next": "
<state-name>
", "TestGroup": "<group-id>
", "TestCases": [ "<test-id>
" ], "ResultVar": "<result-name>
" }
包含值的所有字段都为必填字段,如下所述:
Next
-
在当前状态下执行操作后要过渡到的状态的名称。
TestGroup
-
可选。要运行的测试组的 ID。如果未指定此值,则 IDT 将运行测试运行者选择的测试组。
TestCases
-
可选。来自
TestGroup
中指定的组的测试用例 ID 数组。IDT 根据TestGroup
和TestCases
的值确定测试执行行为,如下所示:-
同时指定
TestGroup
和TestCases
时,IDT 会运行测试组中的指定测试用例。 -
如果
TestCases
已指定但TestGroup
未指定,IDT 将运行指定的测试用例。 -
如果
TestGroup
已指定但TestCases
未指定,则 IDT 将运行指定测试组中的所有测试用例。 -
如果未指定
TestGroup
或TestCases
,IDT 将运行测试运行者从 IDT CLI 中选择的测试组中的所有测试用例。要为测试运行者启用组选择,必须在statemachine.json
文件中同时包含RunTask
和Choice
状态。有关其工作原理的示例,请参阅状态机示例:运行用户选择的测试组。有关为测试运行者启用 IDT CLI 命令的更多信息,请参阅 启用 IDT CLI 命令。
-
ResultVar
-
要与测试运行结果一起设置的上下文变量的名称。如果您没有为指定值,请不要指定此值
TestGroup
。基于以下内容,IDT 将您在ResultVar
中定义的变量的值设置为true
或false
:-
如果变量名称的形式为
,则该值设置为第一个测试组中的所有测试均已通过或跳过。text
_text
_passed -
在所有其他情况下,该值设置为所有测试组中的所有测试已通过或跳过。
-
通常,您将使用 RunTask
状态来指定测试组 ID,而不指定单个测试用例 ID,这样 IDT 就可以运行指定测试组中的所有测试用例。在此状态下运行的所有测试用例均按随机顺序并行运行。但是,如果所有测试用例都需要一台设备运行,并且只有一台设备可用,则测试用例将按顺序运行。
错误处理
如果任何指定的测试组或测试用例 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
数据块来处理错误。如果要在状态机遇到错误时停止执行它,则必须将 FallthroughOnError
设置为 false
。但是,建议您将 FallthroughOnError
设置为 true
,并根据您的用例,执行以下操作之一:
-
如果您正在访问的变量预计在某些情况下不存在,则使用的值
Default
和其他Choices
数据块来指定下一个状态。 -
如果您正在访问的变量应始终存在,则将
Default
状态设置为Fail
。
Parallel
Parallel
状态允许您并行地定义和运行新的状态机。
{ "Type": "Parallel", "Next": "
<state-name>
", "Branches": [<state-machine-definition>
] }
包含值的所有字段都为必填字段,如下所述:
Next
-
在当前状态下执行操作后要过渡到的状态的名称。
Branches
-
要运行的状态机定义阵列。每个状态机定义都必须包含自己的
StartAt
、Succeed
和Fail
状态。此阵列中的状态机定义不能引用其自身定义之外的状态。注意
由于每个分支状态机共享相同的状态机上下文,因此在一个分支中设置变量然后从另一个分支读取这些变量可能会导致意外行为。
只有在 Parallel
运行所有分支状态机之后,该状态才会移动到下一个状态。每种需要设备的状态都将等到设备可用后才运行。如果有多个设备可用,则此状态会并行运行来自多个组的测试用例。如果没有足够的设备可用,则测试用例将按顺序运行。由于测试用例在并行运行时按随机顺序运行,因此可能会使用不同的设备来运行来自同一个测试组的测试。
错误处理
确保分支状态机和父状态机都过渡到 Fail
状态以处理执行错误。
由于分支状态机不会将执行错误传输到父状态机,因此您不能使用 Catch
数据块来处理分支状态机中的执行错误。相反,在共享状态机上下文中使用 hasExecutionErrors
值。有关如何执行此操作的示例,请参阅 状态机示例:并行运行两个测试组。
AddProductFeatures
AddProductFeatures
状态允许您将产品功能添加到 IDT 生成的 awsiotdevicetester_report.xml
文件中。
产品功能是关于设备可能符合的特定标准的用户定义信息。例如,MQTT
产品功能可以指定设备正确发布 MQTT 消息。在报告中,根据指定的测试是否通过,将产品功能设置为 supported
、not-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
。如果未指定此值,则根据测试结果,将特征值设置为supported
或not-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,则将
ResultVar
设置为
。group-id_test-id
_passed -
如果您未指定单个测试用例 ID,则将
ResultVar
设置为
。group-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
_selectedtrue
。请务必提供有效的测试组 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 查询的语法是 {{$.
。在某些状态下,您可以将 JSONPath 查询用作占位符字符串。IDT 将占位符字符串替换为上下文中评估的 jsonPath 查询的值。您可以对占位符使用以下值:query
}}
-
RunTask
状态的TestCases
值。 -
Expression
值Choice
状态。
当您访问状态机上下文中的数据时,请确保满足以下条件:
-
JSON 路径必须以
$.
开头 -
每个值的计算结果必须为字符串、数字或布尔值。
有关使用 JSONPath 表示法从上下文中访问数据的更多信息,请参阅 使用 IDT 上下文。
执行错误
执行错误是状态机在执行状态时遇到的状态机定义中的错误。IDT 在 test_manager.log
文件中记录有关每个错误的信息,并将日志消息流式传输到控制台。
您可以使用以下方法来处理执行错误:
-
在状态定义中添加一个Catch 数据块。
-
在状态机上下文中检查 hasExecutionErrors值。
捕获
要使用 Catch
,请将以下内容添加到您的状态定义中:
"Catch": [ { "ErrorEquals": [ "
<error-type>
" ] "Next": "<state-name>
" } ]
包含值的所有字段都为必填字段,如下所述:
Catch.ErrorEquals
-
要捕获的错误类型的数组。如果执行错误与其中一个指定值匹配,则状态机将转换为
Catch.Next
中指定的状态。有关其产生的错误类型的信息,请参阅每个状态定义。 Catch.Next
-
当前状态遇到与
Catch.ErrorEquals
中指定值之一相匹配的执行错误时,设置要过渡到的下一个状态。
捕获数据块按顺序处理,直到匹配。如果没有错误与捕获数据块中列出的错误相匹配,则状态机将继续执行。由于执行错误是由状态定义不正确造成的,因此我们建议您在状态遇到执行错误时过渡到失败状态。
hasExecutionError
当某些状态遇到执行错误时,除了发出错误外,它们还会在状态机上下文中将 hasExecutionError
值设置为 true
。您可以使用此值来检测何时发生错误,然后使用 Choice
状态将状态机过渡到 Fail
状态。
该方式具有以下特征。
-
状态机不以分配给
hasExecutionError
的任何值启动,并且在特定状态设置该值之前,该值不可用。这意味着必须将访问此值的Choice
状态的FallthroughOnError
明确设置为false
,以防止在没有发生执行错误时状态机停止。 -
一旦将其设置为
true
,hasExecutionError
就永远不会设置为 false 或将其从上下文中删除。这意味着该值仅在首次设置为true
时才有用,并且对于所有后续状态,它不会提供有意义的值。 -
hasExecutionError
值与处于Parallel
状态的所有分支状态机共享,这可能会导致意想不到的结果,具体取决于访问该值的顺序。
由于这些特性,如果您可以改用捕获数据块,我们不建议您使用此方法。
示例状态机
本部分提供了一些状态机配置示例。
状态机示例:运行单个测试组
该状态机:
-
运行带有 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" } } }
状态机示例:并行运行两个测试组
该状态机:
-
并行运行
GroupA
和GroupB
测试组。通过分支状态机中的RunTask
状态存储在上下文中的ResultVar
变量可供AddProductFeatures
状态使用。 -
检查是否存在执行错误,如果发现任何错误,则过渡到
Fail
。此状态机不使用Catch
数据块,因为该方法不会检测分支状态机中的执行错误。 -
根据通过的群组向
awsiotdevicetester_report.xml
文件添加功能-
如果
GroupA
通过,则该功能将设置为supported
。 -
报告中未将该功能标记为可选。
-
-
生成报告,如果没有错误,则过渡到
Succeed
,否则过渡到Fail
如果在设备池中配置了两个设备,则 GroupA
和 GroupB
可以同时运行。但是,如果其中一个 GroupA
或 GroupB
有多个测试,则两个设备都可能分配给这些测试。如果只配置了一台设备,则测试组将按顺序运行。
{ "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" } } }