Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17992] ファイルの概要

このコミットは、Go言語のunicode/utf16パッケージにおいて、無効なUTF-16サロゲートペアのデコードに関する明示的なテストを追加するものです。これにより、既存のDecodeRune関数のテストカバレッジが向上し、特に不正な入力に対する挙動が正しく処理されることを保証します。

コミット

commit 62baae6e57ca9271fc9a4269958d474aa398cc00
Author: Dave Cheney <dave@cheney.net>
Date:   Mon Dec 16 12:35:25 2013 +1100

    unicode/utf16: add explicit test for decoding invalid runes.
    
    The EncodeRune test exercises DecodeRune, but only for runes that it can encode. Add an explicit test for invalid utf16 surrogate pairs.
    
    Bonus: coverage is now 100%
    
    unicode/utf16/utf16.go: IsSurrogate     100.0%
    unicode/utf16/utf16.go: DecodeRune      100.0%
    unicode/utf16/utf16.go: EncodeRune      100.0%
    unicode/utf16/utf16.go: Encode          100.0%
    unicode/utf16/utf16.go: Decode          100.0%
    total:                  (statements)    100.0%
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/39150044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/62baae6e57ca9271fc9a4269958d474aa398cc00

元コミット内容

このコミットの目的は、unicode/utf16パッケージのDecodeRune関数に対して、無効なUTF-16サロゲートペアをデコードする際の挙動を明示的にテストすることです。既存のEncodeRuneテストはDecodeRuneも間接的にテストしていましたが、それはエンコード可能な有効なルーンに限定されていました。この変更により、無効なサロゲートペアが入力された場合に、適切にUnicodeの置換文字(U+FFFD)が返されることを確認するテストが追加されました。結果として、unicode/utf16/utf16.go内の関連関数のテストカバレッジが100%に達しました。

変更の背景

Go言語の標準ライブラリにおけるunicode/utf16パッケージは、UTF-16エンコーディングとデコーディングを扱うための機能を提供します。UTF-16は可変長エンコーディングであり、基本多言語面(BMP)の文字は16ビットの単一コードユニットで表現されますが、それ以外の文字(サロゲートペア)は2つの16ビットコードユニット(上位サロゲートと下位サロゲート)のペアで表現されます。

このコミットが行われた背景には、DecodeRune関数が、有効なサロゲートペアだけでなく、不正な組み合わせのサロゲートペア(例えば、上位サロゲートの後に下位サロゲートではない値が続く場合)をどのように処理するかという点に対するテストの不足がありました。堅牢なソフトウェアにおいては、予期せぬ不正な入力に対しても、定義された適切な挙動(この場合はUnicodeの置換文字U+FFFDを返すこと)を示すことが重要です。

既存のテストは、主に有効なエンコード/デコードパスを検証していましたが、不正な入力に対するエッジケースのテストが不足していたため、このコミットでそのギャップを埋めることが目的とされました。テストカバレッジの向上は、コードの品質と信頼性を高める上で重要な指標となります。

前提知識の解説

UnicodeとUTF-16

  • Unicode: 世界中の文字を統一的に扱うための文字コード標準です。各文字には一意の「コードポイント」(整数値)が割り当てられています。
  • ルーン (Rune): Go言語では、Unicodeのコードポイントを表現するためにrune型(int32のエイリアス)を使用します。
  • UTF-16: Unicodeの文字をバイト列にエンコードするための方式の一つです。
    • 基本多言語面 (BMP): UnicodeのU+0000からU+FFFFまでの範囲の文字を指します。これらの文字はUTF-16では1つの16ビットコードユニットで表現されます。
    • サロゲートペア: BMP外の文字(U+10000からU+10FFFFまでの範囲)を表現するために使用されます。これらは2つの16ビットコードユニットのペアで構成されます。
      • 上位サロゲート (High Surrogate): U+D800からU+DBFFまでの範囲のコードユニット。
      • 下位サロゲート (Low Surrogate): U+DC00からU+DFFFまでの範囲のコードユニット。
      • 有効なサロゲートペアは、必ず上位サロゲートの後に下位サロゲートが続く形式を取ります。

Unicodeの置換文字 (U+FFFD)

Unicodeの置換文字(REPLACEMENT CHARACTER, U+FFFD)は、文字コード変換中にエラーが発生した場合や、不正なバイトシーケンスが検出された場合に使用される特殊な文字です。これは、元の文字を正確に表現できない場合に、その場所を占める「不明な文字」を示すために用いられます。UTF-16のデコードにおいて、不正なサロゲートペア(例えば、上位サロゲートの後に下位サロゲートではない値が続く場合や、下位サロゲートが単独で現れる場合など)が検出された際には、このU+FFFDを返すのが一般的な慣習であり、堅牢な実装の証とされます。

テストカバレッジ

テストカバレッジとは、ソフトウェアのテストがどれだけコードを網羅しているかを示す指標です。100%のカバレッジは、すべてのステートメント、ブランチ、関数などがテストによって実行されたことを意味しますが、これは必ずしもすべてのバグがないことを保証するものではありません。しかし、カバレッジが高いほど、コードの品質と信頼性が高い傾向にあります。このコミットでは、特にエッジケース(不正な入力)に対するテストを追加することで、カバレッジを向上させています。

技術的詳細

このコミットは、src/pkg/unicode/utf16/utf16_test.goファイルに新しいテストケースを追加することで、DecodeRune関数の堅牢性を高めています。

DecodeRune(r1, r2 rune) rune関数は、2つのUTF-16コードユニットr1r2を受け取り、それらが構成するUnicodeルーンを返します。もしr1r2が有効なサロゲートペアを形成しない場合、この関数はUnicodeの置換文字U+FFFDを返すことが期待されます。

追加されたテストケース{0xd800, 'a', 0xfffd}は、この挙動を明示的に検証します。

  • 0xd800は上位サロゲートの範囲(U+D800からU+DBFF)に属します。
  • 'a'(ASCII文字 'a' のルーン値、U+0061)は下位サロゲートの範囲(U+DC00からU+DFFF)に属しません。 したがって、0xd800'a'の組み合わせは不正なサロゲートペアであり、DecodeRune0xfffd(置換文字)を返す必要があります。

このテストの追加により、DecodeRune関数が不正な入力に対して適切にフォールバックし、予期せぬクラッシュや誤った文字の解釈を防ぐことが保証されます。これは、特に外部からの入力(ファイル、ネットワークなど)を扱うシステムにおいて、文字エンコーディングの堅牢性を確保するために非常に重要です。

コアとなるコードの変更箇所

変更はsrc/pkg/unicode/utf16/utf16_test.goファイルにのみ行われています。

--- a/src/pkg/unicode/utf16/utf16_test.go
+++ b/src/pkg/unicode/utf16/utf16_test.go
@@ -100,6 +100,26 @@ func TestDecode(t *testing.T) {
 	}\n
 }\n
 \n
+var decodeRuneTests = []struct {\n
+\tr1, r2 rune\n
+\twant   rune\n
+}{\n
+\t{0xd800, 0xdc00, 0x10000},\n
+\t{0xd800, 0xdc01, 0x10001},\n
+\t{0xd808, 0xdf45, 0x12345},\n
+\t{0xdbff, 0xdfff, 0x10ffff},\n
+\t{0xd800, 'a', 0xfffd}, // illegal, replacement rune substituted\n
+}\n
+\n
+func TestDecodeRune(t *testing.T) {\n
+\tfor i, tt := range decodeRuneTests {\n
+\t\tgot := DecodeRune(tt.r1, tt.r2)\n
+\t\tif got != tt.want {\n
+\t\t\tt.Errorf("%d: DecodeRune(%q, %q) = %v; want %v", i, tt.r1, tt.r2, got, tt.want)\n
+\t\t}\n
+\t}\n
+}\n
+\n
 var surrogateTests = []struct {\n
 \tr    rune\n
 \twant bool\n

コアとなるコードの解説

このコミットで追加された主要なコードは、decodeRuneTestsという構造体のスライスと、それを利用するTestDecodeRune関数です。

  1. decodeRuneTests スライス:

    • これは、DecodeRune関数のテストケースを定義する匿名構造体のスライスです。
    • 各要素は、入力となる2つのルーンr1, r2と、期待される結果のルーンwantを持ちます。
    • 最初の4つのテストケースは、有効なサロゲートペアが正しくデコードされることを確認します。例えば、{0xd800, 0xdc00, 0x10000}は、上位サロゲート0xd800と下位サロゲート0xdc00が組み合わさって、UnicodeコードポイントU+10000(𐀀, Linear B Syllable B008 A)になることをテストしています。
    • 重要な追加: {0xd800, 'a', 0xfffd}というテストケースが追加されました。
      • 0xd800は上位サロゲートです。
      • 'a'は下位サロゲートの範囲外の文字です。
      • この組み合わせは不正なサロゲートペアであるため、DecodeRuneはUnicodeの置換文字0xfffdを返すことが期待されます。このテストケースが、このコミットの核心的な変更点です。
  2. TestDecodeRune 関数:

    • Goのテストフレームワーク(testingパッケージ)に準拠したテスト関数です。
    • decodeRuneTestsスライスをループで反復処理します。
    • 各テストケースについて、DecodeRune(tt.r1, tt.r2)を呼び出し、実際の戻り値gotを取得します。
    • gotが期待される値tt.wantと異なる場合、t.Errorfを呼び出してテスト失敗を報告します。これにより、どのテストケースで、どのような入力に対して、どのような結果が得られ、何が期待されていたかが詳細にログに出力されます。

この追加により、DecodeRune関数が有効なサロゲートペアだけでなく、不正なサロゲートペアに対しても、仕様通りにUnicodeの置換文字を返すという堅牢な挙動を保証できるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語のコードレビューシステム (Gerrit) の該当コミット: https://golang.org/cl/39150044 (現在はGitHubに移行しているため、直接アクセスしてもリダイレクトされるか、情報が見つからない場合がありますが、コミットメッセージに記載されている元のリンクです)
  • Go言語のGitHubリポジトリ: https://github.com/golang/go
  • UTF-16に関する一般的な情報源 (例: MDN Web Docs, 各種プログラミング言語のドキュメント)
  • Unicodeのサロゲートペアに関する一般的な情報源 (例: Unicode標準の関連セクション)
  • テストカバレッジに関する一般的な情報源 (例: Wikipedia, ソフトウェアテストに関する書籍や記事)