今回は、ChatGPTの会話をエクスポートした際にハマった(?)内容と、解決した方法について書きます。
ハマった内容は、ChatGPTの「データのエクスポート」の内容の取得です。
ChatGPTでは、会話の記録をエクスポートすることができます。
このエクスポートは、





という手順で実施します。
最後の画面の選択を実施すると、ダウンロードサイトのURLが記載されたメールが、アカウントのメールアドレスに送信されます。
ところが、届いたメールに記載されたURLをクリックしても、以下のようなjson形式のメッセージが表示されるだけで、メッセージのダウンロードができませんでした。
解決方法は、使用しているメーラーのメッセージをHTML形式で表示できるようにするのみです。
私は、Thunderbirdを使用しているのですが、[表示]-[メッセージの表示形式]-[オリジナルHTML]を選択しました。
その結果、表示が以下のようになりました。
この画面の「データエクスポートのダウンロード」をクリックすることで、チャットの内容がダウンロードされます。
表示形式による動作の違いについて少し調べてみたところ、「HTML形式とプレーンテキストでは、URLのエンコード方法が異なる」「改行処理が異なる」ということでした。
たとえば、今回の問題となっているURLは、「&」という文字列を含みます。
表示形式を「HTML形式」に設定していた場合、この文字列は「&」として扱われます。
しかし「プレーンテキスト形式」では、「&」として扱われます。これにより、正しいアドレス(「&」が「&」に置換されたアドレス)にアクセスできません。
その結果としてデータがエクスポートできず、よくわからないjson形式のデータが返されます。
(エラーとならずにjson形式のデータが返される理由は、わかりませんでした。)
確認のために、「プレーンテキスト形式」のメールに表示されているURLの「&」を「&」に変換したURLにアクセスすると、データがエクスポートできました。
今回は、ChatGPTのデータのエクスポートでハマった内容と、その解決方法について書きました。
原因は、表示形式がプレーンテキストの場合、メッセージ内のハイパーリンクが正しく解釈されないことでした。
気が付いてみれば、実にシンプルであり、「だよね!!」という原因でした。
しかし、そういう内容こそ、なかなか気が付けないものです…。
今回の内容が、誰かの助けになれば幸いです。
ではっ!
]]>どもです。
新しいPCに旧HDDを接続してGitリポジトリを開いた際に、
「repository path is not owned by current user」
というエラーが出た場合の対処方法を紹介します。
前々回の投稿で、新しくPCを購入したことを書きました。
その際に、HDDを旧PCから引き継いだことを書きました。今回は、この旧PCから引き継いだHDDのファイルにアクセスしようとした際にハマった内容と、解決した方法について記載します。
今回ハマった内容は、gitからクローンしたファイルへのアクセスです。
引き継いだHDDの中にある、旧PCで作業をしていたフォルダでgitコマンドを実行した際に、以下のようなエラーが発生しました。
Could not get HEAD hash.
libgit2 returned: repository path 'E:/Blog/WordPress' is not owned by current user.

エラーメッセージを読む限りは、移植したフォルダへのアクセス権がない、というものです。
このエラーは、Gitが「他のユーザーが所有しているディレクトリ」で操作を実行したと判断した場合に発生します。旧PCで作成されたリポジトリを新PCへそのまま移植すると、ファイルの所有者情報が一致せず、Gitが安全でない操作とみなしてしまうことが原因です。
表示されているダイアログの中に、この問題を解決するためのコマンドとして、以下が記載されていました。
git config --global --add safe.directory 'E:/Blog/WordPress'
しかし私の場合には、このコマンドを実行しても問題は解決されませんでした。(コマンド実行後も、同じダイアログが表示されます。)
コマンドラインから git コマンドを実行して対応してみます。エラーに対処するために、Git が「安全でない操作」とみなさないよう、safe.directory に対象ディレクトリを追加します。ダイアログに表示されたコマンドをそのまま実行せず、以下のように修正して実行しました:
git config --global --add safe.directory E:/Blog/WordPress
ダイアログに表示されたコマンドとの違いは、シングルクオーテーションの有無です。ダイアログに表示されたコマンドでは、設定に追加するディレクトリのパスをシングルクオーテーションで囲まれていましたが、実行したコマンドでは、これを削除しています。
もし (2.2) がうまく動作しない場合、設定ファイルを直接編集して暫定的に対処する方法もあります。手順の一例:
[safe] セクションに以下を追加する:directory = *
directory = * を追加するこの対応を行うと git コマンドが実行できるようになりました。コマンドで同様の設定を行う場合は以下のようになります:
git config --global --add safe.directory *
ただしこの設定は注意が必要です。directory = * はすべてのディレクトリを「安全」とみなしてしまうため、万が一他人のリポジトリを誤って操作しても警告が出なくなります。一時的な回避策としては有効ですが、恒久的な対策としては推奨できません。可能であれば、最終的には (2.2) のように対象ディレクトリを個別に指定する方法に戻してください。
今回は、別PCから移植したHDD内の git リポジトリを扱う際に発生した「repository path is not owned by current user」エラーへの対処法を紹介しました。今回紹介したのは私の環境で有効だった手順であり、必ずしも最適・安全な方法を保証するものではありません。実行する際は自己責任でお願いします。
ではっ!
]]>前回の投稿で、新しくPCを購入したことを書きました。
今回は、この新しいPCにツールをインストール、セットアップした際にハマった内容と、その対応について書きます。
ハマったツールは、メーラーである Thunderbird です。
前のPCから、公式の手順 に従って移植したのですが、期待通りに移植ができませんでした。
「移植ができなかった」ことの詳細です。
公式の手順に従えば、Thunderbirdを起動すると、移植元のデータが表示され、閲覧できるはずです。
しかし私の環境ではデータが表示されず、メールのアカウント情報を登録する画面が表示されました。
要するに、前のPCで受信したメールのデータが、新しいPCで読むことができませんでした。
この問題の解決した手順を書きます。
解決のためには、公式の手順に従って移植したデータの中の profiles.ini を編集します。
profiles.ini は、以下のフォルダに格納されていると思います。
C:\Users\xxxx\AppData\Roaming\Thunderbird
※「xxxx」はユーザ名
このファイルの中で、[InstallXXXXXXXXXXXXXXXX] の設定項目の Default の設定を変更します。
設定する項目は、前のPCから移植してきたデータが格納されたフォルダの名前です。
私の場合は、前のPCのデータは 5kofq10m.default というフォルダに格納されています。
そのため、profiles.ini には、以下のように設定します。
この設定を行ったあとにThunderbirdを起動すると、前のPCで受信したデータが確認できました。
また、新しいメールも受信できました。
今回は、新しいPCへのThunderbirdのデータ移行で発生した問題と、その対処方法を紹介しました。
公式の手順に従ってもうまく移行できなかったので、profiles.ini ファイルの設定を見直し、変更することで解決ができました。
しかし今回の方法は、あくまで「私が試して(たまたま)うまくいった」という方法です。
決して、正しい方法を保証するものではありません。
データの移植は、必ず 公式の手順 で行ってください。
うまくいかず、対処の方法がわからなかった場合にのみ、試してみてください。
ただし、手順の実施は自己責任でお願いします。
今回の内容が、誰かの助けになれば幸いです。
ではっ!
先日、PCを新しくしました。
新しいPCは、もちろん自作。今回は新しいPCの構成、およびスペックを書いていきます!
新しいPCの構成は、以下の通りです。
| 区分 | パーツ | 詳細 |
|---|---|---|
| CPU | Core Ultra7 265 2400 | 20Core / 20 Thread |
| マザーボード | PRO Z890-A WIFI (MS-7E32) | WiFi内蔵 |
| メモリ | CFD STANDARD DDR5 PC5-44800 CL46 16GB ×2 | 合計 32GB |
| グラフィックボード | ASUS AMD Radeon RX6600 DUAL RX6600-8G-V3 | – |
| 電源 | Segotep GM750W | – |
| ストレージ | KIOXIA SSD-CK1.0N4P/J | 1TB |
| PCケース | Versa H26 Black /w casefan | CA-1J5-ooM1WN-01 |
OSには、もちろん Win11 Pro を選択しました。
今回のPC、総額およそ ¥189,000 でした。
サブストレージや光学ドライブを前のPCのものを流用し、購入を控えたにもかかわらず、それなりのお値段です…。
この構成の中で気に入っているのが、マザーボードです。
マザーボード「PRO Z890-A WIFI (MS-7E32)」は名前の通り、ボードにWiFiが装備されています。
そのため、USBのWiFiアンテナを使わずにWiFiを使用できます。USBポートを使用せずにWiFiが使えるようになるのは、とてもありがたいです。
久しぶりにPCを購入、自作しました。
今回のPCは、前のPCから追加ストレージや高額ドライブを使いまわすことで、少しパーツを少なくして費用を抑えたと思っていました。にもかかわらず、総額は前回よりも高額となっていました…。
それでも、同等のスペックの完成品を購入する場合には、倍以上の値段となります。PCの組み立てや配線が苦ではないのであれば、自作PCはとても「お得」です。
PCケースを選んでいて気が付いたのですが、最近のPCは光学ドライブを着けるスペースが無いようです。購入したお店の方に質問したところ、ダウンロードが主流となり、光学ドライブは不要になってきている、とのことでした。前のPCを購入した際には、そんな気配はなかったので、時代の流れ、変化を感じました。
加えて、今回購入したPCケースは、側面の1つが透明になっていました。ケースの中にLEDを貼ってビカビカ光らせる予定も、光らせることに興味が全くないので、何一つ魅力的ではないですが…。
新しくしたこの環境で、引き続きいろいろ試していきます。
ではっ!
]]>…なんでみんなPC光らせるんだろう…?
光源って熱源なんじゃないの?
今回は、Google Testを使って実装されている単体テストを、GitHub Actionsで実行される環境を作成してみたので、その内容について書いてみます。
単体テストを自動実行できるようにするにあたり、まずテスト対象とするコードを決定してみます。
サンプルの関数をテキトーに作成してもよいのですが、今回は以下のリポジトリのあるコードを対象としてみることにしてみました。
このコードの中でも、特に以下のフォルダに格納されたコードのいくつかを対象にしてみます。
※だいぶ昔に、LEGOのMindSTORMを動かすために作成したコードです。自分のコードなので、文句は言われない。
このコードをテストするにあたり、以下のようなフォルダ構成を作成します。
(work_space_root)
├─.github
│ └─workflows
└─dev
├─src
│ ├─CLC
│ │ CLC_TravelDistance.cpp
│ │ CLC_TravelSpeed.cpp
│ │
│ ├─include
│ │ ev3api.h
│ │
│ └─UTIL
│ util.cpp
│ util.h
│
└─test
├─framework
│ └─googletest
└─src
│ CMakeLists.txt
│
├─CLC
│ CMakeLists.txt
│ UTEST_TravelSpeed_calc_travel_speed.cpp
│ UTEST_TravelSpeed_init_travel_speed.cpp
│
└─UTIL
CMakeLists.txt
UTEST_util_init_buff.cpp
UTEST_util_limit_int.cpp
この構成の中で、dev/srcの下にテスト対象関数が実装されたソースコードが格納されています。
dev/test/srcの下に、単体テストのコードを配置しています。
このコードは、今回はGoogleTestを使用して実装しています。
単体テストのフレームワークであるGoogleTestは、dev/test/frameworkの下にヘッダファイル、およびライブラリを格納しています。
細かく記載すると長くなるので、本投稿では詳細を割愛します。
また、テストプログラムのビルドにcmakeを使用します。そのために必要なCMakeLists.txtも作成しています。
GoogleTestやcmake、CMakeLists.txt の内容はGitHub Actionsの本質ではないので、内容の説明は省略します。
ただ、/dev/test/srcの下、およびCLCやUTILの各フォルダにおいて、cmakeによるビルドができるようにしておく必要はあります。
開発環境、および作業環境の確認ができたので、次はGitHubActionsの設定を行います。
GitHubActionは、.github/workflows内に、拡張子が.ymlとなっているファイルに記述、定義します。
今回は、CMake_UTest.ymlという名前で作成します。
このファイルの内容は、以下の通りです。
name: CMake Unit Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y cmake g++ make
- name: Configure
run: cmake -S dev/test/src -B dev/test/src/build
- name: Build
run: cmake --build dev/test/src/build
- name: Run tests (build root)
run: cd dev/test/src/build && ctest --output-on-failure
- name: Run tests (UTIL)
working-directory: dev/test/src/build/UTIL
run: ./UTEST_util --gtest_output=xml:utest_util_results.xml
- name: Run tests (CLC)
working-directory: dev/test/src/build/CLC
run: ./UTEST_TravelSpeed --gtest_output=xml:utest_util_results.xml
先頭のnameは、GitHubActionsの名前を指定します。この値が、GitHubのサイトで表示されます。
GitHubActionsの実行タイミングは、on句の中で設定します。
今回の例は、pushあるいはpull_requestが行われた場合に実行されることを示しています。
また、対象のブランチも指定可能で、今回の例ではmainブランチが対象です。
実行する内容はjobsで設定します。
今回の例では、build-and-testがIDに該当します。
runs-onで指定します。今回はubuntu-latestを使用。
steps内に記載します。
usesで再利用可能なアクションを呼び出し、runで実行コマンドを記述。
また、working-directoryで作業ディレクトリを指定できます。
この設定が実行されると、GitHubActionsの画面に結果が表示されます。

各stepを展開して、GoogleTestの出力を確認することも可能です。

今回は、GitHubActionsを設定して、リポジトリにpushされる度にテストが実行される環境の構築を行いました。
基本的な内容だけでも、自動テストの実行と結果確認が可能です。
設定も簡単で、紹介した設定だけでCI環境を構築できました。
CI環境はソフトウェア開発において必須です。これからもGitHubActionsを活用してCI環境を整えていきたいです。
今回の内容が、だれかの役に立てば幸いです。
ではっ!
今回の投稿の内容、および紹介を省略したコード類はGitHubに公開しています。
よろしければ、参考にしてください。
前回までの投稿を通して、C++のクラス(のメンバ関数)のテストダブルを、gmockを使用して宣言/定義してきました。
一方で、C++で実装されたプログラム、アプリケーションでも、C言語形式の関数(グローバル関数)も実装されています。
そのため、gmockでグローバル関数の下位関数(グローバル関数)のテストダブルもgmockで宣言/定義できると、フレームワークの一貫性を保つことができ、テストが分かり易くなり、また保守性も向上します。
(グローバル関数のテストダブルは、独自のフレームワーク、実装になっていると、色々と問題の火種になります。)
使用するツール、フレームワークは全体として一貫していることが望ましいものです。
そこで今回のエントリでは、gmockを使用してグローバル関数のテストダブルを宣言/定義できるのか、できる場合には、どのように実装するのかを見てみます。
作業環境です。
これまでと同様に、以下の環境で作業を行っています。
| 項目 | 内容 |
|---|---|
| CPU | Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.20 GHz |
| RAM | 16.0 GB (15.9 GB 使用可能) |
| OS | Windows 10 Professional 22H2 (19045.5487) |
| IDE | Visual Studio Community 2022 (64bit), Version 17.14.8 |
| CMake | version 4.1.0 |
早速ですが、今回は以下のような関数を例に、検討を行います。
//サンプルコード
int Caller(int x) {
return DoWhenCalled(x); // テスト時に、DoWhenCalled()をテストダブルで置換する。
}
それでは、前出の関数の子関数である Called() をテストダブルの宣言・定義を考えます。
まず、インタフェースです。
ここでは、テストダブル化する子関数と同じI/Fを持つインタフェースを定義します。
具体的には、以下のコードです。
class ICalled {
public:
virtual ~ICalled() = default;
virtual int DoWhenCalled(int x) = 0;
};
次に、このインタフェースの具象クラスです。
このクラスが、モックのクラス(本投稿では、テストダブルとの区別のために、「モッククラス」と呼称します。)となります。
ここでgmockのマクロを使用して、テストダブルを定義します。
これも、これまでの同行の中で紹介してきたコードと同じようなコードです。
class Called : public ICalled {
public:
virtual ~Called() = default;
MOCK_METHOD(int, DoWhenCalled, (int x), (override));
};
この辺は、定型文として覚えておくとよいですね。
今回テストダブル化したい関数である DoWhenCalled() の本体です。
これは、モックのクラスのオブジェクトのメソッドを呼び出すのみになります。
具体的には、以下のコードになります。
int DoWhenCalled(int x)
{
return calledMock->DoWhenCalled(x);
}
モッククラスのインスタンスです。
これは、グローバル変数として宣言し、テストドライバにてインスタンスをセットします。
具体的には、以下のようなコードです。
// モッククラスへのポインタ
ICalled* calledMock;
// モッククラスのセット
Called mock;
calledMock = &mock;
最後に、テストダブルの動作です。
テストダブルの動作は、これまでの投稿の中で紹介してきた内容と同じです。
テストダブル化の対象がグローバル関数となった場合でも、とくに違いはありません。
次に、テストドライバです。
とは言っても、先述した通り、テストドライバは、これまで紹介してきた内容と同様です。
TEST(CalledTest, mockTest_001)
{
int x = 1;
Called mock;
calledMock = &mock;
EXPECT_CALL(mock, DoWhenCalled(1))
.WillOnce(testing::Return(2));
int ret_val = Caller(x);
ASSERT_EQ(2, ret_val);
}
今回は、gmockを使用してグローバル関数のテストダブルを宣言/定義できるのか、できる場合には、どのように実装するのかを見てみました。
その結果、gmockを使用したモッククラスを定義し、そのインスタンス、およびモックとなるメソッドをグローバル関数から呼び出すことで、テストダブル化できることを確認できました。
この際に使用するgmockのマクロ、メソッドに関しても、特別な違いはありませんでした。
違いがあるといえば、モッククラスのメソッドを参照するのが、テスト対象のクラス/メソッドではなく、テストダブル化の対象となるメソッドである、という点です。
この点は、DI(Dependency Injection/依存性注入)で注入する依存性がテストダブル化する子関数に変わった、と考えることができます。
そう考えれば、それほど難しいことではないと思います。
このように、メンバ関数(メソッド)であってもグローバル関数であっても、gmockを用いてテストダブル化することができます。
この内容が、誰かの助けになれば幸いです。
ではっ!
これまでgmockを使用したテストダブルの定義、および動作の指定方法を書いてきました。
これらの記事で、入門レベルの基本的な内容は書けたのかな、と考えています。
ところで、以下に挙げる投稿の中で、スタブ/テストダブルが持つべきと私が考えている機能を提案しています。
gmockが、これらの機能を提供できているのかを考えてみます。
作業環境です。
これまでと同様に、以下の環境で作業を行っています。
| 項目 | 内容 |
|---|---|
| CPU | Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.20 GHz |
| RAM | 16.0 GB (15.9 GB 使用可能) |
| OS | Windows 10 Professional 22H2 (19045.5487) |
| IDE | Visual Studio Community 2022 (64bit), Version 17.14.8 |
| CMake | version 4.1.0 |
まず、スタブ/テストダブルが持つべき機能として、呼び出し回数の保持です。
gmockでは、直接呼出し回数を保持する、ということはできません。
ここで、呼び出し回数は、スタブ/テストダブル化した関数が呼び出された回数を確認できるようにすることが、最大の目的です。
そのため重要なのは、呼び出し回数を保持していることではなく、呼び出し回数を確認できるか否かです。
gmockでは、Times()メソッドを使用することで、対象の関数が呼び出された回数が確認できます。
Times()メソッドを使用して呼び出し回数を確認するサンプルコードは以下の投稿にありますので、参考にしていただければと思います。
gMockについて勉強してみた(1):Windows上でgMockをビルドしてみた
次にスタブ/テストダブルが持つべき機能として、任意の戻り値の指定があります。
gmockでは、testing::Return()メソッドを使用することで、任意の戻り値を設定することができます。
また、スタブ/テストダブルが複数回呼び出される場合は、戻り値の設定方法は複数あります。
呼び出される度に同じ値を返す場合にはWillRepeatedly()メソッドを、異なる値を返す場合にはWillOnce()を使用します。
testing::Return()メソッドを使用したサンプルコードは、以下の投稿にあります。
gmockについて勉強してみた(3):テストダブルの複数回呼び出し
なお、この投稿の中ではWillOnce()メソッドを使用しています。
WillRepeatedly()を使用したサンプルコードはありません。(スミマセン…。)
次に、スタブ/テストダブルに渡された引数の値の保持です。
スタブ/テストダブルの引数の値の保持は、単に「引数で渡された値の保持」と、「ポインタ引数の実体の保持」の2つがあります。
それぞれの引数の値の保持について、gmockでの実現可能性を確認します。
まず、単に「引数で渡された値の保持」の方法です。
gmockによるスタブ/テストダブルの引数に渡された値の保持は、SaveArg()メソッドを使用します。
SaveArg()メソッドは、ポインタ引数を持ちます。
このポインタ引数に、スタブ/テストダブルに渡された値が格納され、引数の値が保持されます。
SaveArg()メソッドを使用したサンプルコードは、以下の投稿にあります。
gmockについて勉強してみた(5):ポインタ引数で渡された値のコピー
SaveArg()メソッドは、スタブ/テストダブルの引数がポインタ/非ポインタであっても使用可能です。
次に、「ポインタ引数の実体の保持」の方法です。
gmockによるスタブ/テストダブルのポインタ引数の実体を保持するためには、Invoke()メソッドを使用します。
ただし、Invoke()メソッド自体が、ポインタ引数の実体を保持してくれるわけではありません。
Invoke()メソッドでは、スタブ/テストダブルが呼び出された際の動作を指定することができます。
この動作に、引数の値を保持する処理を指定することで、ポインタ引数の実体を保持することができます。
Invoke()メソッドを使用したサンプルコードは、以下の投稿にあります。
gmockについて勉強してみた(5):ポインタ引数で渡された値のコピー
次に、スタブ/テストダブルの引数への値の格納です。
このパターンは、ポインタ型かつ出力となる引数を持つメソッドのスタブ/テストダブルが持つべき機能です。
この機能の動作は、ポインタがシングルポインタの場合とダブルポインタの場合で、それぞれ異なっています。
それぞれの場合について、gmockでの実現可能性を確認してみます。
まず、出力となるポインタがシングルポインタの場合の動作です。
この場合、テストダブルは、指定されたアドレスに任意の値を格納する、という動作をする必要があります。
ここで、指定されたアドレスの実体が配列か否かによって、使用するgmockのメソッドが異なります。
アドレスの実体が配列ではない場合は、SetArgPointee()メソッドを使用します。
SetArgPointee()メソッドの第1引数に指定した値が、指定されたアドレスの実体にセットされます。
SetArgPointee()を使用したサンプルコードは、以下の投稿にあります。
gmockについて勉強してみた(2):ポインタ引数を持つメソッドのモック
gmockについて勉強してみた(3):テストダブルの複数回呼び出し
アドレスの実体が配列であった場合には、SetArrayArgument()を使用します。
SetArrayArgument()メソッドの第1引数に、アドレスの実体に格納したい値を保持している領域の先頭アドレスを指定します。
第2引数には、アドレスの実体に格納したい値の末尾のアドレスを指定します。
(「アドレスの実体に格納したい値を保持している領域の末尾」ではないことに注意が必要。)
SetArrayArgument()を使用したサンプルコードは、以下の投稿にあります。
gmockについて勉強してみた(4):ポインタの実体が配列であった場合
次に、出力となるポインタがダブルポインタの場合の動作です。
この場合、テストダブルは、ダブルポインタの実体に、任意の領域のアドレスを格納する、という動作をする必要があります。
そのために使用するgmockもメソッドは、SetArgPointee()です。
はい。
引数がシングルポインタかつ出力であった場合と、同じメソッドを使用します。
「ポインタ引数の実体に、任意の値をセットする」という意味では、シングルポインタでもダブルポインタでも同じ動作になります。
引数がダブルポインタであった場合に、SetArgPointee()を使用したサンプルコードは、以下の投稿にあります。
gmockについて勉強してみた(6):ダブルポインタ引数を持つメソッドのモック
今回は、以前の投稿で提案したスタブ/テストダブルが持つべきと考える機能について、gmockが提供できているか確認しました。
結果として、gmockでも同等の機能を提供、実現できると確認できました。
以前の投稿で提案した内容では、スタブ/テストダブルを全て手動で実装する必要がありました。
しかしgmockでは、提供されているメソッドを使用することで、これらの実装をすることなく同等の内容を実現できることが分かりました。
この記事の内容が、誰かの助けになれば幸いです。
ではっ!
前回の投稿までの中で、ポインタ引数、特にシングルポインタ引数を持つメソッドのモックを定義する内容を書いてきました。
今回は、ポインタ引数、特にダブルポインタ引数を持つメソッドのモックをgmockで定義してみます。
作業環境です。
これまでと同様に、以下の環境で作業を行っています。
| 項目 | 内容 |
|---|---|
| CPU | Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.20 GHz |
| RAM | 16.0 GB (15.9 GB 使用可能) |
| OS | Windows 10 Professional 22H2 (19045.5487) |
| IDE | Visual Studio Community 2022 (64bit), Version 17.14.8 |
| CMake | version 4.1.0 |
私個人の経験ですが、ダブルポインタ引数は出力となります。
そのため、出力となるダブルポインタ引数を持つメソッドを呼び出すメソッドを考えます。
今回は、以下のような配列のアドレスをポインタに格納して返すメソッドを例に、話を進めます。
// テスト対象クラス
class BufferUser {
public:
BufferUser(IAllocator& alloc) : alloc_(alloc) {}
int UseBuffer()
{
int* buffer = nullptr;
alloc_.AllocateBuffer(&buffer);
if (!buffer) return -1;
int sum = 0;
for (int i = 0; i < 3; ++i) {
sum += buffer[i];
}
return sum;
}
private:
IAllocator& alloc_;
};
UseBufferメソッドは、内部で呼び出しているAllocateBuffer()メソッドが返したアドレスの実体(配列)に格納された値の和を返します。
テストダブル化するAllocateBuffer()メソッドは、以下のようなI/Fを持ちます。
void AllocateBuffer(int** out_ptr)
インターフェースも含めれば、以下のようになります。
// インターフェース
class IAllocator {
public:
virtual ~IAllocator() = default;
virtual void AllocateBuffer(int** out_ptr) = 0;
};
このメソッドのテストダブルは、gmockを用いて以下のように定義/実装できます。
// モック
class MockAllocator : public IAllocator {
public:
MOCK_METHOD(void, AllocateBuffer, (int** out_ptr), (override));
};
前出のテストダブルがテスト実行時に呼び出された際の動作を、テストダブルで指定します。
指定したい動作は、予め指定された配列のアドレスを、引数のダブルポインタの実体に格納する、という動作です。
この動作は、gmockのSetArgPointeeメソッドで指定します。
具体的なコードは、以下のようになります。
SetArgPointee<0>(test_data)
ここでtest_dataは、以下のように予め宣言しておきます。
static int test_data[3] = { 1, 2, 3 };
これらのことを踏まえたテストドライバ全体の実装は、以下のようになります。
// テスト
TEST(BufferUserTest, DoublePointerArgument)
{
MockAllocator mock;
// モックが返す配列データ
static int test_data[3] = { 1, 2, 3 };
// out_ptr(int**)の指す先に test_data のアドレスをセット
EXPECT_CALL(mock, AllocateBuffer(_))
.WillOnce(DoAll(
SetArgPointee<0>(test_data) // 0番目の引数(int** out_ptr)に test_data のアドレスを代入
));
BufferUser user(mock);
EXPECT_EQ(user.UseBuffer(), 6); // 1+2+3=6
}
前出のテストドライバ内で使用されているgmockのメソッドの仕様を確認します。
このメソッドは、以前の投稿の中ですでに登場しております。
しかし、その投稿の中では、動作については書きましたが、説明をしていませんでした(ゴメンナサイ)。
なので、ここで改めて説明をします。
SetArgPointeeは、gmockの公式サイトでは、以下のように説明されています。
SetArgPointee<N>(value) N-番目(0基準)の argument によって指される変数に、value を代入します。
この説明は…特段解りにくいということはないので、言い換えは省略します。
今回は、ダブルポインタ引数を持つテストダブルをgmockで定義、実装、動作を指定してみました。
ポインタ引数について、入出力の方向が「出力」であれば、シングルポインタ、ダブルポインタともに「SetArgPointee」メソッドを使用することで、出力する値を指定することができます。
このようにgmockにより、出力となるダブルポインタ引数を持つメソッドのテストダブルを、簡単かつ効率的に定義、実装することができることが分かりました。
この記事の内容が、誰かの助けになれば幸いです。
ではっ!
前回までは、出力となるポインタ引数を持つメソッドのテストダブルをgmockで定義する方法を書いてきました。
今回は、入力となるポインタ引数を持つメソッドのテストダブルを、gmockで定義してみます。
作業環境です。
これまでと同様に、以下の環境で作業を行っています。
| 項目 | 内容 |
|---|---|
| CPU | Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.20 GHz |
| RAM | 16.0 GB (15.9 GB 使用可能) |
| OS | Windows 10 Professional 22H2 (19045.5487) |
| IDE | Visual Studio Community 2022 (64bit), Version 17.14.8 |
| CMake | version 4.1.0 |
入力となるポインタ引数を持つメソッドを呼び出すメソッドを考えます。
今回は、次のような配列を指すポインタ引数を持つメソッドを呼び出すメソッドを例に、話を進めます。
// テスト対象クラス
class TargetClass {
SubFunctionInterface* subfunc_;
public:
explicit TargetClass(SubFunctionInterface* subfunc) : subfunc_(subfunc) {}
void DoSomething(int* ptr, size_t size)
{
subfunc_->SubFunc(ptr, size);
}
};
DoSomething()メソッドは、特に何もしません。
呼び出し元から渡された値を、そのまま下位関数に渡します。
ここで、下位関数であるSubFuncが、テストダブル化の対象となるメソッドです。
テストダブル化するSubFunc()メソッドは、以下のI/Fを持ちます。
void SubFunc(int* ptr, size_t size)
このメソッドのテストダブルは、gmockを用いて以下のように定義/実装できます。
// インターフェース
class SubFunctionInterface {
public:
virtual ~SubFunctionInterface() = default;
virtual void SubFunc(int* ptr, size_t size) = 0;
};
// モッククラス
class MockSubFunction : public SubFunctionInterface {
public:
MOCK_METHOD(void, SubFunc, (int* ptr, size_t size), (override));
};
前出のテストダブルがテスト実行時に呼び出された際の動作を、テストドライバで指定します。
ここで、入力となるポインタ引数に対して、テストダブルがどのように動作するのか、どの値を保持しなければならないのかを確認します。
過去の投稿では、
と、結論づけています。
これに加えて最近では、ポインタの実体の値も保持するようにしています。
(テスト対象関数の出力であるため。この辺の結論の変更は、別途投稿を作成する予定です…。)
「渡されたアドレスを代入/保持する」動作は、gmockのSaveArgメソッドで指定します。
具体的なコードは、以下のようになります。
SaveArg<0>(&captured_ptr)
ここでcaptured_ptrは、予め以下のように定義します。
int* captured_ptr = nullptr;
次に、「ポインタの実体の値も保持する」動作は、以下のように定義します。
Invoke([&captured_values](int* ptr, size_t size) {
captured_values.assign(ptr, ptr + size);
})
これらの処理を、テストダブルの動作を指定するDoAllメソッドの引数に渡します。
DoAll(
SaveArg<0>(&captured_ptr),
SaveArg<1>(&captured_size),
Invoke([&captured_values](int* ptr, size_t size) {
captured_values.assign(ptr, ptr + size);
})
)
captured_valuesは、予め以下のように定義します。
std::vector<int> captured_values;
また、途中でSaveArg()メソッドを使用した、以下のコードが登場しています。
SaveArg<1>(&captured_size)
このコードは、テストダブルの第2引数の値を保持する動作を指定しています。
テストドライバ全体は、以下のようになります。
TEST(GMockPointerArrayCaptureTest, CapturePointerArrayContents)
{
MockSubFunction mock_subfunc;
TargetClass target(&mock_subfunc);
int actual_array[] = { 11, 22, 33, 44 };
size_t actual_size = sizeof(actual_array) / sizeof(actual_array[0]);
int* captured_ptr = nullptr;
size_t captured_size = 0;
std::vector<int> captured_values;
EXPECT_CALL(mock_subfunc, SubFunc(_, _))
.WillOnce(DoAll(
SaveArg<0>(&captured_ptr),
SaveArg<1>(&captured_size),
Invoke([&captured_values](int* ptr, size_t size) {
captured_values.assign(ptr, ptr + size);
})
));
target.DoSomething(actual_array, actual_size);
// ポインタの一致確認
EXPECT_EQ(captured_ptr, actual_array);
EXPECT_EQ(captured_size, actual_size);
// 配列の中身確認
ASSERT_EQ(captured_values.size(), actual_size);
for (size_t i = 0; i < actual_size; ++i) {
EXPECT_EQ(captured_values[i], actual_array[i]);
}
}
SaveArg<N>(pointer)は、「N-番目の引数そのものをコピーして保存する」動作を指定します。
保存先はユーザーが渡した変数のアドレスとなります。
Invoke(f)は、「モック関数に渡された引数を与えて f を呼び出す」動作を定義します。
fはラムダ式で指定できるため、動作を柔軟に制御できます。
今回は、入力となるポインタ引数を持つメソッドのテストダブルをgmockで定義してみました。
定義するテストダブルでは、以下の値を保持するようにしました。
gmockでは、SaveArg() および Invoke() を使用することで、これらを保持するテストダブルが実装できます。
特に Invoke() はラムダ式を利用できるため、柔軟に挙動を指定することが可能です。
このようにgmockにより、入力となるポインタ引数を持つメソッドのテストダブルを定義できることが確認できました。
今回の投稿が、誰かの助けになれば幸いです。
ではっ!
今回は、配列を指すポインタ引数を持つメソッドのテストダブルを、gmockで定義する方法を解説します。実際のコード例を交えながら、実装手順とポイントを紹介していきます。
作業環境です。
これまでと同様に、以下の環境で作業を行っています。
| 項目 | 内容 |
|---|---|
| CPU | Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.20 GHz |
| RAM | 16.0 GB (15.9 GB 使用可能) |
| OS | Windows 10 Professional 22H2 (19045.5487) |
| IDE | Visual Studio Community 2022 (64bit), Version 17.14.8 |
| CMake | version 4.1.0 |
配列を指すポインタ引数を持つメソッドを呼び出すメソッドを考えます。
今回は、次のような配列を指すポインタ引数を持つメソッドを呼び出すメソッドを例に、話を進めます。
// テスト対象クラス
class Processor {
public:
Processor(IDataProvider& provider) : provider_(provider) {}
int Process()
{
int buffer[3] = { 0 };
if (provider_.GetValues(buffer, 3)) {
return buffer[0] + buffer[1] + buffer[2];
}
return -1;
}
private:
IDataProvider& provider_;
};
Process()メソッドが呼び出したGetValues()というメソッドにおいて、引数として渡した配列に値が格納されます。
Process()メソッドは、配列の要素の総和を返します。
このGetValues()が、テストダブル化するメソッドです。
テストダブル化するGetValues()メソッドは、以下のようなI/Fを持つメソッドを考えます。
bool GetValues(int* out, int count)
ポインタ引数outには、GetValues()メソッド内で、出力する値が格納されます。
また、格納処理の実行結果(成功/失敗)が、メソッドの戻り値としてbool値で返します。
このメソッドのテストダブルは、gmockでは以下のように定義/実装できます。
// インターフェース
class IDataProvider {
public:
virtual ~IDataProvider() = default;
virtual bool GetValues(int* out, int count) = 0;
};
// モッククラス
class MockDataProvider : public IDataProvider {
public:
MOCK_METHOD(bool, GetValues, (int* out, int count), (override));
};
前出のテストダブルがテスト実行時に呼び出された際の動作を、テストドライバで指定します。
動作を指定する際には、gmockのSetArrayArgument()メソッドを使用します。具体的には、以下のようなコードです。
SetArrayArgument<0>(arr, arr + 3)
ここでarrは、予め以下のように定義します。
int arr[3] = { 1, 2, 3 };
これらのコードにより、GetValues()メソッドは、テスト実行時に、引数で指定された配列に対して、それぞれ1、2、3、という値を格納します。
結果として、テスト対象のメソッドは、1~3の値の和である6を返します。
この内容をテストするテストドライバ全体の実装は、以下になります。
// テスト
TEST(ProcessorTest, ReturnsSumOfProvidedArray)
{
MockDataProvider mock;
int arr[3] = { 1, 2, 3 };
// GetValues 呼び出し時、out[0..2] に arr の内容をコピーし、true を返す
EXPECT_CALL(mock, GetValues(_, 3))
.WillOnce(DoAll(SetArrayArgument<0>(arr, arr + 3), Return(true)));
Processor proc(mock);
EXPECT_EQ(proc.Process(), 6); // 1+2+3=6
}
配列を指すポインタ引数を持つメソッドが呼び出された際の動作を指定するSetArrayArgument()メソッドを見てみます。
gmockでは、以下のように定義されています。
template <size_t k, typename I1, typename I2>
internal::SetArrayArgumentAction<k, I1, I2> SetArrayArgument(I1 first, I2 last)
この関数は、gmockの公式サイト/チートシートでは、以下のように解説されています。
SetArrayArgument<N>(first, last):入力範囲 [first、 last) の要素を、N-番目(0基準)の argument (これは、ポインタまたはイテレータ)によって指される配列にコピーします。Action は、入力範囲内の要素の所有権を持ちません。
要は、「数の引数として渡された配列ポインタに対して、任意のデータを書き込む」という動作を指定するためのメソッドです。
SetArrayArgument()の基本的な使い方が分かったところで、少し応用について考えてみます。
まず、コピー開始位置を先頭以外に指定してみます。この場合、第1引数、第2引数ともに変更する必要があります。
例えば配列のインデックス1の要素から3要素ぶんコピーする、という動作を指定する場合には、以下のように指定します。
SetArrayArgument<0>(arr + 1, arr + 4)
このことから、「N番目の要素からM個の要素をコピーする」という動作は、以下のように指定します。
SetArrayArgument<0>(arr + N, arr + (N + M))
次に、「<0>」を変更してみます。
ただし今回のテストダブル化するメソッド bool GetValues(int* out, int count) では、テンプレート引数の <0> は「コピー先にする引数のインデックス(0始まり)」を表します。
すなわち <0> は out を、<1> は count を指します。count はポインタでもイテレータでもないため、<1> に変更するとコンパイルエラーになります。
コピー開始位置はテンプレート引数ではなく、関数引数の first/last(例:arr + N、arr + (N + M))で制御します。
今回は、配列を指すポインタ引数を持つメソッドのテストダブルの動作をgmockで指定する方法を確認しました。結果として、以下の方法で設定することができました。
SetArrayArgument() メソッドを使用する。first)を指定する。last)を指定する。
コピー開始位置は任意に設定できるため、柔軟に動作を指定することができます。
そのため、gmockのSetArrayArgument()メソッドを使用することで、配列を指すポインタ引数を持つメソッドのテストダブルを、効率的かつ簡潔に作成できます。
今回の投稿が、誰かの助けになれば幸いです。
ではっ!