排解失敗問題

本指南將概略說明排解 Fuchsia (CTF) 相容性測試失敗問題時的常見情境,並逐一說明造成故障的原因,以及解除封鎖 CL 提交內容的方法。

CTF 測試會斷言在發布分支版本上發生軟體凍結,以及建構於 fuchsia.gitmain 分支的軟體之間的互動。如果這些斷言因不相容而失敗,系統會封鎖提交 CL 的作業。

CTF 的用途並不是禁止破壞相容性造成的異動,而是是為了顯示這類中斷情形,以便正確處理。

情境

每個情況都會發生一個真正的相容性問題,這會導致 CTF 測試失敗,並阻擋 CL 提交作業。相容性問題可以非常簡單,只要將測試使用的現有 FIDL 通訊協定的傳回值變更過即可。我們會假設各種情境的常見情境如下 (如要瞭解更多測試情境,請參閱動機):

  • FIDL 用戶端在 F19 上凍結為 ctf_fuchsia_package,稱為 echo-service-tests。這會連結至 FIDL 通訊協定並斷言回應。

  • generate_ctf_tests.gni 包含規則 template("generate_echo-service-tests"),會將傳入的用戶端套件與在 main 上建構的伺服器套件合併。(詳情請參閱使用手冊)。

  • 用戶端會在伺服器上呼叫 Echo 方法,該方法會做為輸入字串並傳回字串,如下所示:

    auto server_proxy = connect_to_named_protocol("my_protocol.EchoService");
    ASSERT_EQ(
      server_proxy.echo("Hello"),
      "Hello"
    );
    

假設我們想變更 echo 的行為,改為傳回所傳遞字串的小寫表示法。我們可以在 main 上更新測試,以便執行下列操作:

ASSERT_EQ(
  server_proxy.echo("Hello"),
  "hello"
);

通過 ctf_in_development 測試,但對這個新伺服器執行使用舊斷言的凍結用戶端時,測試會失敗:

FAILURE: "hello" != "Hello"

目前已封鎖提交 CL 的功能,而且後續路徑取決於這次中斷的原因。

非蓄意破壞性變更 - 柔和轉換

在這種情況下,我們無意造成相容性中斷。如果預先建構或樹狀結構外元件依附舊行為,就會發生這種情況。針對包含 Echo 變更的平台執行這些元件,會導致非預期的行為。

在這種情況下,安全措施是輕柔轉換到新行為:

  1. 推出以新行為的新方法:

    protocol Echo {
     // ...
     @available(added=20)
     EchoLowercase(struct {input string}) -> (string);
    };
    
  2. 將舊方法標示為已淘汰或已移除 (選用):

    protocol Echo {
     @available(removed=20)
     Echo(/* ... */) -> (string);
     // ...
    };
    
  3. 在伺服器中實作新方法。

  4. fuchsia.git 呼叫端變更為使用新方法,而非舊方法。

伺服器必須支援這兩種方法,直到系統不再支援所有 API 級別為止。在上述範例中,由於舊的 Echo 方法已在 F20 中移除 (原因為 @available(removed=20)),因此不支援 F19。

蓄意破壞性變更 - 發布分支版本中的變更測試

在這種情況下,相容性中斷是刻意的。這可能是由以下原因造成:

  • 我們想變更 API 的行為,並接受預先建構或樹狀結構外用戶端中斷的風險。此為實質性失敗,我們將明確確認這一點。

  • 變更的行為僅供測試內部使用,並不代表 SDK 介面本身的破壞性變更。這會是偽陽性失敗,因為我們發現到測試不相容,而不是 SDK 不相容。

無論是哪一種情況,解決方案都是修改版本分支,如此一來,當針對 main 上的變更前或後變更程式碼執行時,測試將不再失敗。

變更執行方式如下:

  1. 查看版本分支:

    fx sync-to refs/heads/releases/f19
    
  2. 修改斷言,使其接受兩種輸出內容 (或加上註解):

    EXPECT_THAT(
     server_proxy.echo("Hello"),
     AnyOf(Eq("Hello"), Eq("hello"))
    );
    
  3. 測試您的變更是否能在 main生效 (請見下方說明)

  4. 修訂並推送變更:

    git push origin HEAD:refs/for/releases/f19
    
  5. 將 CL 送交審核並提交。

  6. 已禁止封鎖 CL。

  7. 清除發布分支版本,以便僅接受新行為 (選用)。

    EXPECT_EQ(
      server_proxy.echo("Hello"),
      "hello"
    );
    

您可以測試變更套用至 main 後是否能正常運作。您需要兩個 Fuchsia 的結帳,一個與版本分支版本同步,另一個則同步至 main。請完成下列步驟:

  1. 發布分支版本結帳頁面,建立新的 CTF 套件。

    fx set core.x64 --with-tests //sdk/ctf
    fx build
    
  2. main 結帳頁面中,建構 CTF 版本測試。

    fx set core.x64 --with-tests //sdk/ctf/release:tests
    fx build
    
  3. 版本分支版本的輸出目錄中,將建構的 CTF 組合複製到「main存放區。

    cp -fR \
    $RELEASE_BRANCH_FUCHSIA_OUT_DIR/cts/* \
    $MAIN_BRANCH_FUCHSIA_DIR/prebuilt/ctf/f19/linux-x64/cts/
    
  4. 重新建構 main 結帳功能、重新貼上裝置或重新啟動模擬器,然後執行測試。

  5. 通過測試後,還原至 CIPD 的版本。

    jiri run-hooks
    

範例:SDK 相容性中斷 (真陽性)

https://fxrev.dev/1041178 導入了 fuchsia.ui.policy.MediaButtonsListener 通訊協定的實際相容性中斷問題。新參數會新增至 MediaButtonsEvent,並提供正確的版本管理註解,但如果參數保留為空白,且自先前的實作方式改為空白,該行為的行為就會變更。CTF 針對 F19 的測試找出了這種不相容性:

../../src/ui/tests/conformance_input_tests/media-button-validator.cc:243: Failure
Expected equality of these values:
  ToString(listener.events_received()[0])
    Which is: "\n    volume: 0\n    mic_mute: 0\n    pause: 0\n    camera_disable: 0\n    power: 0"
  ToString(MakePowerEvent())
    Which is: "\n    volume: 0\n    mic_mute: 0\n    pause: 0\n    camera_disable: 0\n    power: 1"

系統判定可以接受這項變更,因為這個通訊協定沒有任何指定 API 級別 19 的預先建構用戶端。

已按照上方的操作說明,將 CL https://fxrev.dev/1044994 和 https://fxrev.dev/1049612 挑選進 F19 發布分支版本中。一旦變更內容導入 main 使用的 CTF 版本後,原始 CL 就會未經修改便提交。

範例:測試控管工具相容性故障 (偽陽性)

https://fxrev.dev/1007583 造成 fuchsia.tracing.controller.Controller 通訊協定的相容性中斷。先前 StopTracing 方法設為 flexible,而將方法從 strict 變更為 flexible 並不會安全 ABI。

這項變更發生時,WLAN hw-sim CTF 測試會當機,因為該測試會聲明在測試期間成功停止追蹤,即使這與測試的 WLAN 通訊協定無關,也不必進行更正。這是偽陽性相容性失敗,因為這只會影響測試控管本身。

被發現 https://fxrev.dev/1014607 會將追蹤失敗變成警告,而不是嚴重的斷言,而這項變更是獲選為 F18 版本分支所挑選的。一旦在 main 使用的 CTF 版本推出變更後,原始 CL 就不會進行任何修改。

刻意捨棄對特定測試的 API 級別支援

在本情境中,我們想要在每次測試時明確捨棄對 API 級別的支援。雖然 version_history.json 提供支援 API 級別的標準清單,但我們基於理由而想要捨棄特定 API 級別上特定通訊協定的相容性保證。例如,可以接受破壞舊用戶端的非關鍵子系統重大重構。

在這種情況下,我們可以修改程序,略過指定想要捨棄的 API 級別的測試:

  1. 修改 main 分支版本上的 generate_ctf_tests.gni,有條件地略過測試。

    template("generate_my-service-tests") {
     forward_variables_from(invoker, [ "test_info" ])
     if (defined(invoker.api_level) && invoker.api_level == "15") {
       # Do not thaw this test for F15
       not_needed([ "test_info" ])
       group(target_name) {
       }
     } else {
       // ...
     }
    }
    
  2. 修訂並提交這個 CL。

上述範例在 F15 時略過整個構件。如果您不想略過所有測試,請參閱上一節,瞭解如何在版本分支版本中略過個別測試案例。

範例:主要重構來診斷子系統

https://fxrev.dev/1019042 刪除對不再使用的 DirectoryReady 元件事件的支援。主要用途是支援從元件 (現在使用 fuchsia.inspect.InspectSink 通訊協定發布) 取得診斷資料。

遺憾的是,以 F15 建構的元件仍會使用 DirectoryReady 發布診斷結果,而且在移除 main 後,所有針對該行為的 CTF 診斷測試都會失敗。

幸好,並沒有針對 F15 的預先建構元件,我們需要取得診斷資料。CL 提交給 Google 停用所有源自 F15 的 CTF 測試。