その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Test::Time と日付系モジュールを同時に使う際の注意

cho45 さんが作られた Test::Time と日付系モジュール (DateTime や Time::Piece など) と同時に使用する場合には注意が必要です。

Test::Time の動作を補足的に解説しますと、このモジュールは sleep() と time() を上書きしています。
sleep() が呼ばれると、本来ならば引数として与えられた秒数だけスリープしますが、
Test::Time を use しているとスリープされません (つまり即座にsleep() は処理を終える)。
その代わりに、引数として sleep() に与えられていた秒数を time() によって得られる秒数に加えるという動作を行います。
...日本語で説明するのがなんだか難しいので、参考コードを掲示します。

こんな感じです。おわかり頂けるでしょうか。


で。
例えば以下の様なコードは期待通りに動作しません。
(今回の例では DateTime を用いていますが、適宜 Time::Piece に読み替えてもらっても差し支えありません)

2回目の`say $time;`では`sleep 60;` という記述によって 60 秒加算された時間が出力されそうな感じがしますが、そうはなりません。
どうすれば期待通りの動作を実現できるかと言うと、以下のように書き換えると良いです。

やったことは至って単純で、`use DateTime;` と `use Test::Time;` の呼び出し順を変更しただけです。
日付系モジュールよりも先に`use Test::Time;` を呼んでやることによって期待通りの動作を実現できます。

なぜこんなことが起こるのか

前述の通り、Test::Time は sleep() と time() を上書きしています。
Test::Time のソースコードを読むと分かるように、この上書き処理はCORE::GLOBAL::sleep と CORE::GLOBAL::time を上書きすることによって実現しています。

で、ここらへんの挙動はCORE - perldoc.perl.org を読むと分かるんですが、実際それを読むよりも以下に引用するtokuhirom さんの Post がわかりやすいので掲示します。


なので、日付系モジュールよりも先に Test::Time を読み込んでおかなければ、日付系モジュールが上書きされた time() を使うことが出来ないって話だったんですね。