MQTTサーバーを実装しながらGoを学ぶ - その4 テストカバレッジ

前回の続きです。今回は、MQTTのCONNECTパケットのペイロードから。

目次。

CONNECTパケットのペイロード

ペイロードには5つのデータが含まれる。

  • Client Identifier
  • Will Topic
  • Will Message
  • UserName
  • Password

この中のClient Identifierは必須。残りの4つは可変ヘッダーのConnect Flagsの値次第で必要かどうかが決まる。Connect Flagsについては後回しにしているので、ペイロードでもClient Identifierだけをとりあえずは実装する。

http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718031

Client Identifier

仕様を読むとClient Identifierはこんな感じ。

  • クライアント毎にユニーク
  • クライアントとサーバー間のセッションを維持するために使う
  • ペイロードの先頭
  • Section 1.5.3で定義されたUTF-8エンコーディングされた文字列
  • さらに条件がある
    • 1〜23byte
    • 使える文字は 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
    • 以下のように書いてあるけど、許可しないでおく
      • The Server MAY allow ClientId’s that contain more than 23 encoded bytes. The Server MAY allow ClientId’s that contain characters not included in the list given above.
      • A Server MAY allow a Client to supply a ClientId that has a length of zero bytes
  • 不正なClient Identifierだった場合、サーバーはクライアントにCONNACKパケット(return codeは0x02)を返して、コネクションを切断する

上記の仕様を実装していく。

binaryパッケージ

Client Identifierはペイロードの先頭で、Section 1.5.3で定義されたUTF-8エンコーディングされた文字列なので、ペイロードの先頭の2バイト分がClient Identifierの長さになる。バイト列と数値の変換は binary パッケージが使える。

binary - The Go Programming Language

ビッグエンディアンなのでlength := binary.BigEndian.Uint16(payload[0:2]) とすればClient Identifierの長さが取得できる。

regexpパッケージ

次は文字の種類をチェック。正規表現を使う。

regexp - The Go Programming Language

これでOK!

$ go test ./packet/
ok      github.com/bati11/oreno-mqtt/study/packet       1.283s

テストのカバレッジ

go test でテストを実行してきたが、オプションをつけるとカバレッジを取得できるらしい。しかも -cover オプションをつけるだけ。

The cover story - The Go Blog

試してみる。

$ go test -cover ./packet/
ok      github.com/bati11/oreno-mqtt/study/packet       1.270s  coverage: 97.6% of statements

97.6%。さっきの文字種類のテストコードを削ってみる。

実行。

$ go test -cover ./packet/
ok      github.com/bati11/oreno-mqtt/study/packet       0.655s  coverage: 95.2% of statements

95.2%に下がった。

もっと詳細に調べてみる。 -coverprofile=cover.out という指定をすると、cover.outというファイルができて、そのファイルを go tool cover コマンドでプロファイリングできる。

$ go test -coverprofile=cover.out ./packet/
ok      github.com/bati11/oreno-mqtt/study/packet       0.019s  coverage: 95.2% of statements
$ go tool cover -func=cover.out
github.com/bati11/oreno-mqtt/study/packet/connect_payload.go:16:                ToConnectPayload        83.3%
github.com/bati11/oreno-mqtt/study/packet/connect_variable_header.go:23:        ToConnectVariableHeader 100.0%
github.com/bati11/oreno-mqtt/study/packet/connect_variable_header.go:41:        isValidProtocolName     100.0%
github.com/bati11/oreno-mqtt/study/packet/fixed_header.go:14:                   ToFixedHeader           100.0%
github.com/bati11/oreno-mqtt/study/packet/fixed_header.go:35:                   refbit                  100.0%
github.com/bati11/oreno-mqtt/study/packet/fixed_header.go:40:                   decodeRemainingLength   100.0%
total:                                                                          (statements)            95.2%

ふむふむ、connect_payload.goの ToConnectPayload のカバレッジが低めだということが分かる。

さらにさらに、 -html=cover.out と指定するとブラウザで確認することもできる。

$ go tool cover -html=cover.out

https://i.gyazo.com/5e2b68026888cf2537364dc5f0c2a41d.png

す、すごい...。

他にも -covermode というオプションがあり、実行された回数まで含めてプロファイルもできそう。

カバレッジ見てたら、指定された長さに対して実際に取得できるClient Identifierの長さが足りない場合のテストが足りないことに気がついたので追加。さっき消したテストも戻す。

go test -coverprofile=cover.out ./packet/
ok      github.com/bati11/oreno-mqtt/study/packet       0.022s  coverage: 100.0% of statements
 go tool cover -func=cover.out
github.com/bati11/oreno-mqtt/study/packet/connect_payload.go:16:                ToConnectPayload        100.0%
github.com/bati11/oreno-mqtt/study/packet/connect_variable_header.go:23:        ToConnectVariableHeader 100.0%
github.com/bati11/oreno-mqtt/study/packet/connect_variable_header.go:41:        isValidProtocolName     100.0%
github.com/bati11/oreno-mqtt/study/packet/fixed_header.go:14:                   ToFixedHeader           100.0%
github.com/bati11/oreno-mqtt/study/packet/fixed_header.go:35:                   refbit                  100.0%
github.com/bati11/oreno-mqtt/study/packet/fixed_header.go:40:                   decodeRemainingLength   100.0%
total:                                                                          (statements)            100.0%

$ go test --help を読むと以下のように書いてある。

-cover
    Enable coverage analysis.
    Note that because coverage works by annotating the source
    code before compilation, compilation and test failures with
    coverage enabled may report line numbers that don't correspond
    to the original sources.

-cover をつけない状態でテストして、PASSしてからカバレッジを取得する方が良さそうだ。

おしまい

MQTTのCONNECTパケットのペイロードを実装して、テストのカバレッジを取得しました。次回はサーバーとして起動するところまでいきたい。

今回の学び