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

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

本コミットは、Go言語の実験的なHTMLパーサーパッケージ exp/html におけるテストの挙動を修正するものです。具体的には、テストケースの入力処理において、末尾の改行文字の扱いをより厳密にすることで、これまで失敗していた2つのテストケースが正しくパスするように改善しています。

コミット

commit 2f39a33b6a34cbc71ded902053cbd10447b073dc
Author: Andrew Balholm <andybalholm@gmail.com>
Date:   Fri Aug 3 09:31:45 2012 +1000

    exp/html: in parse tests, discard only one trailing newline
    
    Pass 2 additional tests.
    
    R=nigeltao
    CC=golang-dev
    https://golang.org/cl/6454090

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

https://github.com/golang/go/commit/2f39a33b6a34cbc71ded902053cbd10447b073dc

元コミット内容

exp/html: in parse tests, discard only one trailing newline Pass 2 additional tests.

このコミットは、exp/html パッケージのパーステストにおいて、末尾の改行文字を1つだけ破棄するように変更し、それによって2つの追加テストがパスするようになったことを示しています。

変更の背景

HTMLパーサーのテストは、非常に厳密な入力と期待される出力に基づいて行われます。特に、空白文字や改行文字の扱いは、パース結果に大きな影響を与える可能性があります。

このコミットが行われる前は、exp/html パッケージのテストユーティリティが、テストケースの入力文字列から末尾の改行文字を「すべて」削除する strings.TrimRight 関数を使用していました。しかし、特定のテストケースでは、入力データの一部として意図的に複数の改行が含まれていたり、あるいはテストファイルのフォーマットとして末尾に単一の改行が存在することが期待されていた可能性があります。

strings.TrimRight(s, "\n") は、文字列 s の末尾から連続するすべての改行文字を削除します。例えば、"foo\n\n""foo" に、"foo\n""foo" になります。この挙動が、テストケースの意図と合致しない場合、本来パスすべきテストが失敗するという問題が発生していました。

本コミットは、この過剰な改行削除が原因で失敗していた2つのテストケースを修正するために導入されました。テスト入力の処理方法を、末尾に改行が存在する場合に「1つだけ」削除するように変更することで、テストの意図に沿った正確な入力がパーサーに渡されるようになり、結果としてテストがパスするようになりました。

前提知識の解説

  • Go言語の exp/html パッケージ: これはGo言語の標準ライブラリの一部として提供される前の、実験的なHTMLパーサーパッケージです。HTML5の仕様に準拠したHTMLドキュメントのパース機能を提供します。現在では golang.org/x/net/html が推奨されていますが、このコミット当時は exp/html が開発中でした。
  • HTMLパース: HTMLパースとは、HTMLドキュメントのテキストデータを読み込み、それをブラウザがレンダリングできるような構造化されたデータ(通常はDOMツリー)に変換するプロセスです。このプロセスには、トークン化(文字ストリームを意味のある単位であるトークンに分割する)とツリー構築(トークンからDOMツリーを構築する)が含まれます。空白文字や改行文字の扱いは、HTMLの仕様上、非常に複雑であり、パーサーの正確性に直結します。
  • Go言語の strings パッケージ: Go言語の標準ライブラリの一部で、文字列操作のためのユーティリティ関数を提供します。
    • strings.TrimRight(s, cutset string): 文字列 s の末尾から、cutset に含まれる文字をすべて削除した新しい文字列を返します。例えば、strings.TrimRight("hello\n\n", "\n")"hello" を返します。
    • strings.HasSuffix(s, suffix string): 文字列 ssuffix で終わる場合に true を返します。
  • Go言語のテスト: Go言語では、_test.go というサフィックスを持つファイルにテストコードを記述します。go test コマンドで実行され、テストの成功/失敗がログに出力されます。本コミットで変更されている testlogs/*.dat.log ファイルは、これらのテスト実行結果を記録したログファイルであると考えられます。

技術的詳細

本コミットの核心は、src/pkg/exp/html/parse_test.go ファイル内の readParseTest 関数における文字列処理の変更です。この関数は、HTMLパーステストの入力データを読み込む役割を担っています。

変更前は、入力されたバイトスライス b を文字列に変換した後、strings.TrimRight(string(b), "\n") を用いて末尾の改行文字をすべて削除していました。

// 変更前
text = strings.TrimRight(string(b), "\n")

この TrimRight の挙動が、特定のテストケースで問題を引き起こしていました。テストデータによっては、入力の末尾に単一の改行が存在することが期待されており、それがテストフレームワークによって削除されるべき「フォーマット上の改行」なのか、それともパーサーが処理すべき「コンテンツの一部としての改行」なのかの区別が曖昧になっていました。TrimRight は後者の場合でも改行を削除してしまい、パーサーに渡される入力がテストの意図と異なるものになっていたのです。

変更後は、この挙動が以下のように修正されました。

// 変更後
text = string(b)
if strings.HasSuffix(text, "\n") {
    text = text[:len(text)-1]
}

この新しいロジックでは、まずバイトスライス b をそのまま文字列 text に変換します。次に、strings.HasSuffix(text, "\n") を使って、文字列が少なくとも1つの改行文字で終わっているかを確認します。もし終わっていれば、text[:len(text)-1] を使って、文字列の末尾から「1つだけ」改行文字を削除します。

この変更により、例えば入力が "foo\n\n" であった場合、変更前は "foo" になっていたのが、変更後は "foo\n" となります。これにより、テストケースが期待する正確な入力がパーサーに渡されるようになり、これまで失敗していたテストがパスするようになりました。

src/pkg/exp/html/testlogs/tests16.dat.logsrc/pkg/exp/html/testlogs/tests26.dat.log の変更は、この修正が実際にテスト結果に与えた影響を示しています。これらのログファイルでは、以前 FAIL となっていた特定のテストケースが、末尾に \n が追加された状態で PASS に変更されています。これは、テスト入力の処理方法が変更された結果、パーサーが期待通りの出力を生成できるようになったことを明確に示しています。

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

src/pkg/exp/html/parse_test.go ファイルの readParseTest 関数内。

--- a/src/pkg/exp/html/parse_test.go
+++ b/src/pkg/exp/html/parse_test.go
@@ -45,7 +45,10 @@ func readParseTest(r *bufio.Reader) (text, want, context string, err error) {
 		}
 		b = append(b, line...)
 	}
-	text = strings.TrimRight(string(b), "\n")
+	text = string(b)
+	if strings.HasSuffix(text, "\n") {
+		text = text[:len(text)-1]
+	}
 	b = b[:0]
 
 	// Skip the error list.

また、テストログファイルも変更されています。

src/pkg/exp/html/testlogs/tests16.dat.log

--- a/src/pkg/exp/html/testlogs/tests16.dat.log
+++ b/src/pkg/exp/html/testlogs/tests16.dat.log
@@ -186,6 +186,6 @@ PASS "<iframe><!--<iframe></iframe>--></iframe>"
 PASS "<iframe>...<!--X->...<!--/X->...</iframe>"
 PASS "<xmp><!--<xmp></xmp>--></xmp>"
 PASS "<noembed><!--<noembed></noembed>--></noembed>"
-FAIL "<!doctype html><table>"
+PASS "<!doctype html><table>\n"
 PASS "<!doctype html><table><td><span><font></span><span>"
 PARSE "<!doctype html><form><table></form><form></table></form>"

src/pkg/exp/html/testlogs/tests26.dat.log

--- a/src/pkg/exp/html/testlogs/tests26.dat.log
+++ b/src/pkg/exp/html/testlogs/tests26.dat.log
@@ -7,7 +7,7 @@ PASS "<!DOCTYPE html><body><b><nobr>1<nobr></b><div><i><nobr>2<nobr></i>3"
 PASS "<!DOCTYPE html><body><b><nobr>1<nobr><ins></b><i><nobr>"
 PASS "<!DOCTYPE html><body><b><nobr>1<ins><nobr></b><i>2"
-FAIL "<p><code x</code></p>"
+PASS "<p><code x</code></p>\n"
 PASS "<!DOCTYPE html><svg><foreignObject><p><i></p>a"
 PASS "<!DOCTYPE html><table><tr><td><svg><foreignObject><p><i></p>a"
 PASS "<!DOCTYPE html><math><mtext><p><i></p>a"

コアとなるコードの解説

readParseTest 関数は、テストデータファイルからHTMLの入力文字列を読み取る際に使用されます。

変更前のコード text = strings.TrimRight(string(b), "\n") は、読み込んだバイト列 b を文字列に変換し、その文字列の末尾から連続するすべての改行文字 (\n) を削除していました。これは、テストデータファイルの末尾に余分な改行が複数あっても、それらをすべて取り除くことを意図していました。

しかし、この「すべて削除する」という挙動が、特定のテストケースで問題を引き起こしました。テストケースによっては、入力の末尾に単一の改行が存在することが、テストの期待する入力の一部として意図されていた可能性があります。TrimRight はそのような単一の改行も削除してしまい、パーサーに渡される入力がテストの意図と異なっていました。

変更後のコードは、この問題を解決するために、よりきめ細やかな改行処理を導入しています。

  1. text = string(b): まず、読み込んだバイト列 b をそのまま文字列 text に変換します。この時点では、末尾の改行はすべて保持されています。
  2. if strings.HasSuffix(text, "\n"): 次に、text が少なくとも1つの改行文字 (\n) で終わっているかどうかを確認します。
  3. text = text[:len(text)-1]: もし text が改行で終わっていれば、文字列の最後の1文字(つまり、末尾の改行文字)だけを削除します。

このロジックにより、入力文字列の末尾に複数の改行があったとしても、削除されるのは「最後の1つだけ」になります。例えば、入力が "foo\n\n" であれば、text"foo\n" になります。入力が "foo\n" であれば、text"foo" になります。これにより、テストケースが期待する正確な入力がパーサーに渡されるようになり、これまで失敗していたテストが正しくパスするようになりました。

テストログファイルの変更は、このコード変更が実際にテストの成功に繋がったことを示しています。FAIL から PASS への変更と、期待される出力に \n が追加されていることは、パーサーが改行を含む入力を正しく処理できるようになったことを裏付けています。

関連リンク

参考にした情報源リンク