設定 IDT 狀態機器 - 免費RTOS

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

設定 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-name 物件包含映射至 的有效狀態定義 state-name.

States 物件必須包含 SucceedFail 狀態。如需有效狀態的資訊,請參閱 有效狀態和狀態定義

有效狀態和狀態定義

本節說明可在 狀態機器中使用的所有有效IDT狀態的狀態定義。下列某些狀態支援測試案例層級的組態。不過,除非絕對必要,否則建議您在測試群組層級設定狀態轉換規則,而非測試案例層級。

RunTask

RunTask 狀態會從測試套件中定義的測試群組執行測試案例。

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

如下所述,包含值的所有欄位皆為必要:

Next

在目前狀態下執行動作後,要轉換至 的狀態名稱。

TestGroup

選用。要執行的測試群組 ID。如果未指定此值,請IDT執行測試執行器選取的測試群組。

TestCases

選用。IDs 來自 中指定群組的測試案例陣列TestGroup。根據 TestGroup和 的值TestCases, IDT 決定測試執行行為,如下所示:

  • TestCases 指定 TestGroup和 時, 會從測試群組IDT執行指定的測試案例。

  • TestCases 指定TestGroup但未指定 時, 會IDT執行指定的測試案例。

  • 指定 TestCases 但未指定 TestGroup 時, 會IDT執行指定測試群組中的所有測試案例。

  • 如果沒有指定 TestCases TestGroup或 ,請從測試執行器從 中選取的測試群組IDT中執行所有測試案例IDTCLI。若要啟用測試執行器的群組選取,您必須在statemachine.json檔案中同時包含 RunTaskChoice 狀態。如需如何運作的範例,請參閱 狀態機器範例:執行使用者選取的測試群組

    如需為測試執行器啟用IDTCLI命令的詳細資訊,請參閱 啟用IDTCLI命令

ResultVar

使用測試執行結果設定的內容變數名稱。如果您未指定 的值,請勿指定此值TestGroup。IDT false會根據下列項目,將您在 中定義的變數值ResultVar設定為 true或 :

  • 如果變數名稱為 形式 text_text_passed,則值會設定為是否通過或略過第一個測試群組中的所有測試。

  • 在所有其他情況下,該值會設定為是否所有測試群組中的所有測試都通過或略過。

一般而言,您將使用 RunTask 狀態來指定測試群組 ID,而不指定個別測試案例 IDs,以便 IDT執行指定測試群組中的所有測試案例。此狀態執行的所有測試案例都會以隨機順序平行執行。但是,如果所有測試案例都需要裝置才能執行,而且只有單一裝置可用,則測試案例將改為依序執行。

錯誤處理

如果任何指定的測試群組或測試案例IDs無效,則此狀態會發出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 狀態可讓您彼此平行定義和執行新的狀態機器。

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

如下所述,包含值的所有欄位皆為必要:

Next

在目前狀態下執行動作後,要轉換至 的狀態名稱。

Branches

要執行的狀態機器定義陣列。每個狀態機器定義都必須包含自己的 StartAtSucceedFail 狀態。此陣列中的狀態機器定義無法參考其定義以外的狀態。

注意

由於每個分支狀態機器共用相同的狀態機器內容,因此在某個分支中設定變數,然後從另一個分支讀取這些變數可能會導致意外行為。

Parallel 狀態只有在執行所有分支狀態機器之後才會移至下一個狀態。需要裝置的每個狀態都會等待執行,直到裝置可用為止。如果有多個裝置可用,此狀態會從多個群組平行執行測試案例。如果沒有足夠裝置可用,則測試案例將依序執行。由於測試案例在平行執行時以隨機順序執行,因此可能會使用不同的裝置從相同的測試群組執行測試。

錯誤處理

確定分支狀態機器和父狀態機器都會轉換為 Fail 狀態,以處理執行錯誤。

由於分支狀態機器不會將執行錯誤傳輸至父狀態機器,因此您無法使用Catch區塊來處理分支狀態機器中的執行錯誤。而是在共用狀態機器內容中使用 hasExecutionErrors值。如需如何運作的範例,請參閱 狀態機器範例:平行執行兩個測試群組

AddProductFeatures

AddProductFeatures 狀態可讓您將產品功能新增至 產生的awsiotdevicetester_report.xml檔案IDT。

產品功能是有關裝置可能符合的特定條件的使用者定義資訊。例如,MQTT產品功能可以指定裝置正確發佈MQTT訊息。在報告中,產品功能會根據是否通過指定的測試,設定為 not-supportedsupported或自訂值。

注意

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

選用。測試群組 的陣列IDs。每個指定測試群組中的所有測試都必須通過,才能支援此功能。

OneOfGroups

選用。測試群組 的陣列IDs。至少其中一個指定測試群組中的所有測試都必須通過,才能支援此功能。

TestCases

選用。測試案例 的陣列IDs。如果您指定此值,則適用下列條件:

  • 所有指定的測試案例都必須通過,才能支援此功能。

  • Groups 只能包含一個測試群組 ID。

  • OneOfGroups 不得指定。

IsRequired

選用。設定為 false,以在報告中將此功能標記為選用功能。預設值為 true

ExecutionMethods

選用。符合device.json檔案中指定protocol值的執行方法陣列。如果指定此值,則測試執行器必須指定符合此陣列中其中一個值protocol的值,才能將 功能包含在報告中。如果未指定此值,則功能一律會包含在報告中。

若要使用 AddProductFeatures 狀態,您必須將 RunTask 狀態ResultVar中的 值設定為下列其中一個值:

  • 如果您指定了個別測試案例 IDs,則將 ResultVar設定為 group-id_test-id_passed

  • 如果您未指定個別測試案例 IDs,則將 ResultVar設定為 group-id_passed

AddProductFeatures 狀態會以下列方式檢查測試結果:

  • 如果您未指定任何測試案例 IDs,則每個測試群組的結果都會根據狀態機器內容中的group-id_passed變數值來決定。

  • 如果您確實指定了測試案例 IDs,則每個測試的結果取決於狀態機器內容中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中設定為 。請務必提供有效的測試群組,IDs因為 IDT 不會驗證指定的群組是否存在。

失敗

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

如果測試執行器選取要執行的特定測試群組,而不是整個測試套件,則會建立此金鑰並包含特定測試群組的清單IDs。

specificTestCases

如果測試執行器選取要執行的特定測試案例,而不是整個測試套件,則會建立此金鑰,並包含特定測試案例的清單IDs。

hasExecutionErrors

狀態機器啟動時不會結束。如果任何狀態遇到執行錯誤,則會建立此變數並在狀態機器執行的true剩餘持續時間內設定為 。

您可以使用JSONPath記號來查詢內容。狀態定義中JSONPath查詢的語法為 {{$.query}}。您可以在某些狀態下使用JSONPath查詢作為預留位置字串。IDT 會將預留位置字串取代為內容中評估JSONPath查詢的值。您可以使用預留位置作為下列值:

  • 狀態TestCases的值RunTask

  • ExpressionChoice狀態。

當您從狀態機器內容存取資料時,請確定符合下列條件:

  • 您的JSON路徑必須以 開頭 $.

  • 每個值都必須評估為字串、數字或布林值。

如需使用JSONPath註釋從內容存取資料的詳細資訊,請參閱 使用IDT內容

執行錯誤

執行錯誤是狀態機器在執行狀態時遇到的狀態機器定義錯誤。IDT 會記錄test_manager.log檔案中每個錯誤的相關資訊,並將日誌訊息串流至主控台。

您可以使用下列方法來處理執行錯誤:

擷取

若要使用 Catch,請將下列項目新增至您的狀態定義:

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

如下所述,包含值的所有欄位皆為必要:

Catch.ErrorEquals

要擷取的錯誤類型陣列。如果執行錯誤符合其中一個指定的值,狀態機器便會轉換為 中指定的狀態Catch.Next。如需其產生的錯誤類型的相關資訊,請參閱每個狀態定義。

Catch.Next

如果目前狀態遇到符合 中指定值之一的執行錯誤,則要轉換為Catch.ErrorEquals的下一個狀態。

擷取區塊會依序處理,直到有一個相符項目為止。如果沒有錯誤符合擷取區塊中列出的錯誤,則狀態機器會繼續執行。由於執行錯誤是不正確狀態定義的結果,我們建議您在狀態遇到執行錯誤時,轉換為失敗狀態。

hasExecutionError

當某些狀態遇到執行錯誤時,除了發出錯誤之外,也會在狀態機器內容true中將hasExecutionError值設定為 。您可以使用此值來偵測何時發生錯誤,然後使用 Choice 狀態將 狀態機器轉換為 Fail 狀態。

此方法具有下列特性。

  • 狀態機器不會以指派給 的任何值開始hasExecutionError,而且此值在特定狀態設定之前無法使用。這表示您必須false針對存取此值Choice的狀態,明確將 FallthroughOnError 設為 ,以防止狀態機器在未發生執行錯誤時停止。

  • 設定為 後true,絕不hasExecutionError會設定為 false 或從內容中移除。這表示此值只有在第一次設定為 時才有用true,而且對於所有後續狀態,它不會提供有意義的值。

  • hasExecutionError值會與 Parallel 狀態中的所有分支狀態機器共用,這可能會導致意外的結果,具體取決於存取它的順序。

由於這些特性,如果您可以改用擷取區塊,我們不建議您使用此方法。

狀態機器範例

本節提供一些範例狀態機器組態。

狀態機器範例:執行單一測試群組

此狀態機器:

  • 執行 ID 為 的測試群組GroupA,該 ID 必須存在於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" } } }

狀態機器範例:平行執行兩個測試群組

此狀態機器:

  • 平行執行 GroupAGroupB 測試群組。狀態可使用 分支狀態機器中RunTask狀態儲存在內容中的ResultVar變數AddProductFeatures

  • 檢查執行錯誤,如果找到任何 Fail,則轉換為 。此狀態機器不會使用Catch區塊,因為該方法不會偵測分支狀態機器中的執行錯誤。

  • 根據傳遞的群組,將功能新增至 awsiotdevicetester_report.xml 檔案

    • 如果GroupA通過,則功能會設定為 supported

    • 該功能在報告中不會標記為選用。

  • Succeed 如果沒有錯誤,則產生報告並轉換為 ,Fail否則

如果在裝置集區中設定了兩個裝置,則 GroupAGroupB都可以同時執行。不過,如果 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" } } }