Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

単体テストで文字列リソースを利用できるようにする #1275

Merged

Conversation

berryzplus
Copy link
Contributor

(必須) PR の目的

単体テストで文字列リソースを利用できるようにします。

(必須) カテゴリ

  • その他の問題

(自明なら省略可) PR の背景

#1273 (comment)

(自明なら省略可) PR のメリット

(なければ省略可) PR のデメリット (トレードオフとかあれば)

(わかる範囲で) PR の影響範囲

(なければ省略可) 関連 issue, PR

(なければ省略可) 参考資料

@m-tmatma
Copy link
Member

m-tmatma commented May 6, 2020

英語版リソースも同様にテストするのは可能ですか?
多分同じ EXE にできないので、EXE を分けることになると思いますが。

@beru
Copy link
Contributor

beru commented May 6, 2020

テストコード側で実際のリソースを読めないと困る事ってあるんでしょうか?
コード側で指定したリソースデータがちゃんと存在して読めるかの確認にはなるかもしれないですが…。

@berryzplus
Copy link
Contributor Author

なんでやねん!w

と思ったことを正直に書いておきます。

@AppVeyorBot
Copy link

@berryzplus
Copy link
Contributor Author

英語版リソースも同様にテストするのは可能ですか?
多分同じ EXE にできないので、EXE を分けることになると思いますが。

言語切替は CSelectLang が担当しています。CSelectLang の仕様に基づいて英語版リソースを適切に配置してやればテストコードから英語版リソースを読むことは可能です。

CSelectLang に英語DLLを読み込ませるには、DLLSHAREDATAを初期化する必要があります・・・要するに、コントロールプロセスを起動する必要があります。目下ここが大きな課題です。

DLLSHAREDATAへの依存問題さえ解決できれば、あとはsakura_lang_en_US.dllと同じフォルダにtests1.exeを配置して英語版でも同様のテストを実施できます。

@berryzplus
Copy link
Contributor Author

テストコード側で実際のリソースを読めないと困る事ってあるんでしょうか?
コード側で指定したリソースデータがちゃんと存在して読めるかの確認にはなるかもしれないですが…。

たとえば CSelectLang の初期化コードには、exeにリソースが埋め込まれている前提の assert があるので単体テスト書くなら埋め込んでおかないと困りますね。

// 言語情報ダイアログで "System default" に表示する文字列を作成する
auto nCount = ::LoadString( psLangInfo->hInstance, STR_SELLANG_NAME, psLangInfo->szLangName, _countof(psLangInfo->szLangName) );
assert(0 < nCount);
// 言語IDを取得
WCHAR szBuf[7]; // "0x" + 4桁 + 番兵
nCount = ::LoadString( psLangInfo->hInstance, STR_SELLANG_LANGID, szBuf, _countof(szBuf));
assert(nCount == _countof(szBuf) - 1);
szBuf[_countof(szBuf) - 1] = L'\0';

102行目と108行目のassertです。
このPRで入れようとしてるテスト対象リソースが読めなかったら落ちるようになっています。

ということで、「これ入れないと CControlProcess のテストが書けない」って程度には困る感じです。

Copy link
Contributor

@beru beru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

動作確認しました。問題ないと思います。

@@ -101,6 +101,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<Link Include="$(SolutionDir)sakura\$(Platform)\$(Configuration)\*.obj" />
<Link Include="$(SolutionDir)sakura\$(Platform)\$(Configuration)\*.res" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これだけでリソースを組み込むことが出来るんですね。目から鱗です。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rcをコンパイルしたファイルの拡張子が.objじゃなかったので組み込まれてなかった感じです。実は、MinGW版のtests1.exeにはかなり前から組み込まれていました。

{
// リソースから言語識別子のIDを読み取る
// ここのリソース文字列値は、言語選択のキーなので変更してはならない。
ASSERT_STREQ( L"0x0411", LS( STR_SELLANG_LANGID ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このPRの変更内容には関係ないコメントです。

LS マクロは CLoadString::LoadStringSt の呼び出しになるのでそこからの処理を追ってみました。CLoadString::CLoadStrBuffer::LoadString を見ると色々とやっているんですね。単にWindowsAPIを呼び出しているだけかと思ってました…。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

裏側がかなり高度なリサイクルバッファになっていたはず。

リソース文字列はUTF16LEのWORDシーケンスとして格納されるので、本来的にバッファを用意してコピーする必要はないんですが、NUL終端文字列として扱えるようにするために再利用可能なバッファの仕組みが組み込んである感じです。

他のリソース種類と異なり、RT_STRINGは単なるデータの塊なので、std::wstring_view で直接参照してしまえばバッファ問題は解決できるんじゃないかとか、何気にやや危険なことを前々から思っていたりします。。。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::basic_string_view って NULL終端してなくても良いんですね。部分を参照するのに使われる事があるからそういう仕様なんですね。
https://stackoverflow.com/a/41722032/4699324

ただ format 系の処理で今使っているのは printf 系の関数なのでそちらがNULL終端に対応している事を要求しますね。言語的にNULL終端の流れは切れなそう…。

なおリソース文字列の長さを取るには LoadStringW を使わないやり方が必要なようです。
https://devblogs.microsoft.com/oldnewthing/20040130-00/?p=40813

リソース文字列の容量ってそんな大きくないだろうから起動時に全部メモリに入れてしまった方が使う度にいちいちメモリ確保してコピーとか避けられるんじゃとか思いますが、実現しようと取り掛かってやっと分かる事も多そうです。

@beru
Copy link
Contributor

beru commented May 6, 2020

テストコード側で実際のリソースを読めないと困る事ってあるんでしょうか?
コード側で指定したリソースデータがちゃんと存在して読めるかの確認にはなるかもしれないですが…。

たとえば CSelectLang の初期化コードには、exeにリソースが埋め込まれている前提の assert があるので単体テスト書くなら埋め込んでおかないと困りますね。

stub を用意する必要があるかと思ったらそんな事は無くて簡単な変更でリソースを組み込めたんですね。本物の実装をそのまま使えるならそれがベストですね。

@berryzplus
Copy link
Contributor Author

レビューありがとうございます。
マージしちゃいます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants