[インデックス 10065] ファイルの概要
このコミットは、Go言語の実験的パッケージであるexp/winfsnotify
の単体テストの修正を行ったものです。Windows上でファイルシステム監視を行うためのライブラリのテストにおいて、ファイル同期操作の順序を変更することで、テストの信頼性向上を図っています。
コミット
- コミットハッシュ:
9e1a2adf07cb58a0244d1aebf11da368750cd698
- 作成者: Hector Chu hectorchu@gmail.com
- 日付: 2011年10月20日 08:10:58 +0100
- コミットメッセージ: "exp/winfsnotify: fix test"
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9e1a2adf07cb58a0244d1aebf11da368750cd698
元コミット内容
commit 9e1a2adf07cb58a0244d1aebf11da368750cd698
Author: Hector Chu <hectorchu@gmail.com>
Date: Thu Oct 20 08:10:58 2011 +0100
exp/winfsnotify: fix test
R=alex.brainman, mattn.jp
CC=golang-dev
https://golang.org/cl/5311047
---
src/pkg/exp/winfsnotify/winfsnotify_test.go | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/src/pkg/exp/winfsnotify/winfsnotify_test.go b/src/pkg/exp/winfsnotify/winfsnotify_test.go
index edf2165c0e..8c53fc8de1 100644
--- a/src/pkg/exp/winfsnotify/winfsnotify_test.go
+++ b/src/pkg/exp/winfsnotify/winfsnotify_test.go
@@ -70,15 +70,11 @@ func TestNotifyEvents(t *testing.T) {
if _, err = file.WriteString("hello, world"); err != nil {
t.Fatalf("failed to write to test file: %s", err)
}
- if err = file.Sync(); err != nil {
- t.Fatalf("failed to sync test file: %s", err)
- }
- expect(t, watcher.Event, testFile, FS_MODIFY)
- expect(t, watcher.Event, testFile, FS_MODIFY)
-
if err = file.Close(); err != nil {
t.Fatalf("failed to close test file: %s", err)
}
+ expect(t, watcher.Event, testFile, FS_MODIFY)
+ expect(t, watcher.Event, testFile, FS_MODIFY)
if err = os.Rename(testFile, testFile2); err != nil {
t.Fatalf("failed to rename test file: %s", err)
変更の背景
このコミットは、Windows上でファイルシステム監視を行うexp/winfsnotify
パッケージの単体テストTestNotifyEvents
の修正を行っています。具体的には、ファイルの書き込み操作後に行われるfile.Sync()
呼び出しとその後のexpect()
呼び出しの順序を変更することで、テストの実行時に発生していた問題を解決しています。
修正前のテストでは、ファイルの書き込み直後にfile.Sync()
を呼び出してファイルシステムへの強制書き込みを行い、その後に2回のFS_MODIFY
イベントを期待していました。しかし、Windows上でのファイルシステム監視では、file.Sync()
の実行タイミングとファイルシステムイベントの発生タイミングの間に競合状態が発生し、テストが不安定になっていました。
前提知識の解説
Windows ファイルシステム監視
Windows上でファイルシステムの変更を監視するには、主にReadDirectoryChangesW
Win32 APIを使用します。このAPIは、指定されたディレクトリで発生するファイルシステムの変更を非同期で監視し、変更が発生した際にイベントを通知します。
exp/winfsnotify パッケージ
exp/winfsnotify
は、Go言語の実験的パッケージとして提供されていたWindows専用のファイルシステム監視ライブラリでした。このパッケージは以下の機能を提供していました:
- Watcher: ファイルやディレクトリの変更を監視するメインの構造体
- NewWatcher: 新しいWatcherインスタンスを作成
- AddWatch: 監視対象のファイルやディレクトリを追加
- Watch: 全種類のイベントを監視
重要な注意点: exp/winfsnotify
パッケージは2016年以降メンテナンスされておらず、現在は非推奨となっています。Go 1.17以降の環境では、代わりにgithub.com/fsnotify/fsnotify
パッケージの使用が推奨されています。
file.Sync()の動作
file.Sync()
は、GoのファイルI/Oにおいて重要な役割を果たすメソッドです:
- 目的: ファイルの内容をメモリからストレージデバイスに強制的に書き込む
- 実装: Unix系OSでは
fsync()
システムコール、WindowsではFlushFileBuffers()
を使用 - 保証: システムクラッシュや電源断が発生しても、データが永続化されることを保証
ファイルシステムイベントのタイミング
Windows上でファイルシステムイベントが発生するタイミングは、以下の要因により複雑になります:
- バッファリング: OSによるファイルI/Oバッファリング
- 非同期処理:
ReadDirectoryChangesW
による非同期イベント通知 - タイミング競合: ファイル操作とイベント通知の間の競合状態
技術的詳細
修正前の問題点
修正前のテストコードでは、以下の順序で処理が行われていました:
file.WriteString("hello, world")
- ファイルへの書き込みfile.Sync()
- ファイルの強制同期expect(t, watcher.Event, testFile, FS_MODIFY)
- 1回目の変更イベント期待expect(t, watcher.Event, testFile, FS_MODIFY)
- 2回目の変更イベント期待file.Close()
- ファイルクローズ
この順序では、file.Sync()
の実行により強制的にファイルシステムへの書き込みが行われますが、Windows上のReadDirectoryChangesW
によるイベント通知は非同期で行われるため、expect()
の実行タイミングとイベントの発生タイミングの間に競合状態が発生していました。
修正後の改善点
修正後のコードでは、以下の順序に変更されています:
file.WriteString("hello, world")
- ファイルへの書き込みfile.Close()
- ファイルクローズexpect(t, watcher.Event, testFile, FS_MODIFY)
- 1回目の変更イベント期待expect(t, watcher.Event, testFile, FS_MODIFY)
- 2回目の変更イベント期待
この変更により、以下の改善が達成されました:
- 明確な完了シグナル:
file.Close()
により、ファイル操作の完了が明確になる - タイミング安定化: ファイルクローズ後にイベントを期待することで、競合状態を回避
- テスト信頼性向上: より予測可能なイベント発生タイミング
コアとなるコードの変更箇所
変更されたのはsrc/pkg/exp/winfsnotify/winfsnotify_test.go
ファイルのTestNotifyEvents
関数内の一部です:
変更前(削除された部分)
if err = file.Sync(); err != nil {
t.Fatalf("failed to sync test file: %s", err)
}
expect(t, watcher.Event, testFile, FS_MODIFY)
expect(t, watcher.Event, testFile, FS_MODIFY)
変更後(追加された部分)
if err = file.Close(); err != nil {
t.Fatalf("failed to close test file: %s", err)
}
expect(t, watcher.Event, testFile, FS_MODIFY)
expect(t, watcher.Event, testFile, FS_MODIFY)
コアとなるコードの解説
expect()関数の動作
expect()
関数は、ファイルシステム監視のテストにおいて、期待されるイベントが発生することを検証するヘルパー関数です:
expect(t, watcher.Event, testFile, FS_MODIFY)
- 第1引数:
*testing.T
- テストコンテキスト - 第2引数:
watcher.Event
- イベントチャネル - 第3引数:
testFile
- 対象ファイル名 - 第4引数:
FS_MODIFY
- 期待されるイベント種別
FS_MODIFYイベント
FS_MODIFY
は、ファイルの内容が変更された際に発生するイベントです。Windows上では、以下の操作で発生する可能性があります:
- ファイルへの書き込み操作
- ファイルの属性変更
- ファイルのタイムスタンプ更新
2回のFS_MODIFYイベントの理由
テストで2回のFS_MODIFY
イベントを期待している理由:
- 書き込み操作:
file.WriteString()
による実際のデータ書き込み - メタデータ更新: ファイルサイズやタイムスタンプの更新
Windows上では、単一のファイル操作が複数のファイルシステムイベントを発生させることがあるため、このような多重イベントの検証が重要です。
file.Close()の重要性
file.Close()
は、単なるファイルディスクリプタの解放以上の役割を果たします:
- バッファフラッシュ: 未書き込みデータの強制書き込み
- リソース解放: システムリソースの適切な解放
- 完了シグナル: ファイル操作の完了を示すシグナル
関連リンク
- Go言語公式ドキュメント - os.File.Sync
- Go言語公式ドキュメント - os.File.Close
- github.com/fsnotify/fsnotify - 現在推奨されるファイルシステム監視ライブラリ
- Windows API Reference - ReadDirectoryChangesW
- Windows API Reference - FlushFileBuffers
参考にした情報源リンク
- winfsnotify package - golang.org/x/exp/winfsnotify
- GitHub - fsnotify/fsnotify: Cross-platform filesystem notifications for Go
- x/exp/winfsnotify: delete package · Issue #51447 · golang/go
- Stack Overflow - When to flush a file in Go?
- fsnotify/backend_windows.go at main · fsnotify/fsnotify
- correct way to determine when file writes are finished · Issue #553 · fsnotify/fsnotify