Open32

memo: broccoli

wipesealswipeseals

async関数を持ったtraitのMock化、公開を含めるとごちゃごちゃしてきた

#[cfg_attr(test, async_mock)]
#[cfg_attr(test, async_trait)]
#[trait_variant::make(Send + Sync)]
pub trait Driver {
//...
wipesealswipeseals

USB Mass storage class Bulk-only transportについて

  • ざっくり Command Transport, Data Transport, Status Transportの3ステージになっている
  • 転送には受信、送信のEndpointを1つずつ持つ
  • Command TransportではCBW(CommandTransportWrapper)をHost->Deviceに送り、次のData Transportの情報(データの方向含む)、コマンドの内容を取得する
  • Data TransportではCBWにあった方向(Host->Device or Device->Host)に指定されたデータ数転送。Device->Hostの場合、指定数に満たないケースもあるが、これはStatus Transport時に報告
  • Status Transport: CSW (Command Status Wrapper)をDevice->Hostに転送。コマンドの成否、DataResidue(転送数に満たなかった分の値)、CBWのtagをつけて送る

この他にコントロール転送で Mass storage Reset, Get Max LUNもサポートする必要がある様子 (Bulk onlyなのはtypなデータ転送本体だけ)

wipesealswipeseals

rstest + tokio::test を使ったunit test

   Compiling broccoli-core v0.3.0 (E:\repos\broccoli\broccoli-core)
error[E0433]: failed to resolve: use of undeclared crate or module `async_std`
   --> src\storage\handler_ramdisk.rs:149:5
    |
149 |     #[rstest]
    |     ^^^^^^^^^ use of undeclared crate or module `async_std`
    |
    = note: this error originates in the attribute macro `rstest` (in Nightly builds, run with -Z macro-backtrace for more info)

の怒られがあるが、rstest,async-std (featuresにattributesを追加) で回避できた

[dev-dependencies]
tokio = { version = "1.38.0", features = ["full"] }
async-mock = "0.1.3"
fake = "2.9.2"
mockall = "0.12.1"
rstest = "0.22.0"
async-std = { version = "1.13.0", features = ["attributes"] }
#[cfg(test)]
mod tests {
    use super::*;
    use rstest::rstest;

    type StorageRequestTag = u32;

    const LOGICAL_BLOCK_SIZE: usize = 512;
    const TOTAL_DATA_SIZE: usize = 1024;

    #[rstest]
    #[tokio::test]
    #[case(
        StorageRequest::setup(0x00),
        StorageResponse::report_setup_success(0x00, TOTAL_DATA_SIZE / LOGICAL_BLOCK_SIZE)
    )]
    #[case(
        StorageRequest::read(0x01, 0),
        StorageResponse::read(0x01, [0; 512])
    )]
    #[case(
        StorageRequest::write(0x02, 0, [0; 512]),
        StorageResponse::write(0x02)
    )]
    #[case(
        StorageRequest::read(0x03, 0),
        StorageResponse::read(0x03, [0; 512])
    )]
    #[case(StorageRequest::flush(0x04), StorageResponse::flush(0x04))]
    async fn test_check_id_tag(
        #[case] req: StorageRequest<StorageRequestTag, LOGICAL_BLOCK_SIZE>,
        #[case] expected_resp: StorageResponse<StorageRequestTag, LOGICAL_BLOCK_SIZE>,
    ) {
        let mut handler = RamDiskHandler::<LOGICAL_BLOCK_SIZE, TOTAL_DATA_SIZE>::new();

        let resp = handler.request(req).await;
        assert_eq!(resp, expected_resp);
    }
}

wipesealswipeseals

memo
結局ペリフェラルに依存しないものを外部クレートに切り出す方針なら、arm buildするappには単体テストでテストしたいものはペリフェラル依存のものが多く残るはず

モックでペリフェラルに投げつけるところまでは見れるが、そもそも投げ方を勘違いしているケースなど検証としては片手落ち。qemuでテストできるものが多くない、またペリフェラルを模倣した何らかのプラジェクトがないのであれば、実機向けのテストビルドをして実機上で流した方が良いかもしれない