diff --git a/sakura_core/_os/CClipboard.cpp b/sakura_core/_os/CClipboard.cpp index 6353f7fe79..d5d94f5b65 100644 --- a/sakura_core/_os/CClipboard.cpp +++ b/sakura_core/_os/CClipboard.cpp @@ -152,7 +152,7 @@ bool CClipboard::SetText( if( bColumnSelect ){ UINT uFormat = ::RegisterClipboardFormat( L"MSDEVColumnSelect" ); if( 0 != uFormat ){ - hgClipMSDEVColumn = ::GlobalAlloc( + hgClipMSDEVColumn = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, 1 ); @@ -170,7 +170,7 @@ bool CClipboard::SetText( if( bLineSelect ){ UINT uFormat = ::RegisterClipboardFormat( L"MSDEVLineSelect" ); if( 0 != uFormat ){ - hgClipMSDEVLine = ::GlobalAlloc( + hgClipMSDEVLine = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, 1 ); @@ -186,7 +186,7 @@ bool CClipboard::SetText( if( bLineSelect ){ UINT uFormat = ::RegisterClipboardFormat( L"VisualStudioEditorOperationsLineCutCopyClipboardTag" ); if( 0 != uFormat ){ - hgClipMSDEVLine2 = ::GlobalAlloc( + hgClipMSDEVLine2 = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, 1 ); @@ -276,7 +276,7 @@ bool CClipboard::GetText(CNativeW* cmemBuf, bool* pbColumnSelect, bool* pbLineSe //矩形選択や行選択のデータがあれば取得 if( NULL != pbColumnSelect || NULL != pbLineSelect ){ UINT uFormat = 0; - while( ( uFormat = ::EnumClipboardFormats( uFormat ) ) != 0 ){ + while( ( uFormat = EnumClipboardFormats( uFormat ) ) != 0 ){ // Jul. 2, 2005 genta : check return value of GetClipboardFormatName WCHAR szFormatName[128]; if( ::GetClipboardFormatName( uFormat, szFormatName, _countof(szFormatName) - 1 ) ){ @@ -425,7 +425,7 @@ static CLIPFORMAT GetClipFormat(const wchar_t* pFormatName) bool CClipboard::IsIncludeClipboradFormat(const wchar_t* pFormatName) { CLIPFORMAT uFormat = GetClipFormat(pFormatName); - if( ::IsClipboardFormatAvailable(uFormat) ){ + if( IsClipboardFormatAvailable(uFormat) ){ return true; } return false; @@ -508,7 +508,7 @@ bool CClipboard::SetClipboradByFormat(const CStringRef& cstr, const wchar_t* pFo case 0: nulLen = 0; break; default: nulLen = 0; break; } - HGLOBAL hgClipText = ::GlobalAlloc( + HGLOBAL hgClipText = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, nTextByteLen + nulLen ); @@ -521,7 +521,7 @@ bool CClipboard::SetClipboradByFormat(const CStringRef& cstr, const wchar_t* pFo memset( &pszClip[nTextByteLen], 0, nulLen ); } ::GlobalUnlock( hgClipText ); - ::SetClipboardData( uFormat, hgClipText ); + SetClipboardData( uFormat, hgClipText ); return true; } @@ -563,7 +563,7 @@ bool CClipboard::GetClipboradByFormat(CNativeW& mem, const wchar_t* pFormatName, if( uFormat == (CLIPFORMAT)-1 ){ return false; } - if( !::IsClipboardFormatAvailable(uFormat) ){ + if( !IsClipboardFormatAvailable(uFormat) ){ return false; } if( nMode == -2 ){ @@ -576,10 +576,10 @@ bool CClipboard::GetClipboradByFormat(CNativeW& mem, const wchar_t* pFormatName, } return bret; } - HGLOBAL hClipData = ::GetClipboardData( uFormat ); + HGLOBAL hClipData = GetClipboardData( uFormat ); if( hClipData != NULL ){ bool retVal = true; - const BYTE* pData = (BYTE*)::GlobalLock( hClipData ); + const BYTE* pData = (BYTE*)GlobalLock( hClipData ); if( pData == NULL ){ return false; } @@ -633,10 +633,6 @@ bool CClipboard::GetClipboradByFormat(CNativeW& mem, const wchar_t* pFormatName, return false; } -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // -// staticインターフェース // -// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- // - //! クリップボード内に、サクラエディタで扱えるデータがあればtrue bool CClipboard::HasValidData() { @@ -663,14 +659,14 @@ CLIPFORMAT CClipboard::GetSakuraFormat() } //!< クリップボードデータ形式(CF_UNICODETEXT等)の取得 -int CClipboard::GetDataType() +int CClipboard::GetDataType() const { //扱える形式が1つでもあればtrue // 2013.06.11 GetTextの取得順に変更 - if(::IsClipboardFormatAvailable(GetSakuraFormat()))return GetSakuraFormat(); - if(::IsClipboardFormatAvailable(CF_UNICODETEXT))return CF_UNICODETEXT; - if(::IsClipboardFormatAvailable(CF_OEMTEXT))return CF_OEMTEXT; - if(::IsClipboardFormatAvailable(CF_HDROP))return CF_HDROP; + if(IsClipboardFormatAvailable(GetSakuraFormat()))return GetSakuraFormat(); + if(IsClipboardFormatAvailable(CF_UNICODETEXT))return CF_UNICODETEXT; + if(IsClipboardFormatAvailable(CF_OEMTEXT))return CF_OEMTEXT; + if(IsClipboardFormatAvailable(CF_HDROP))return CF_HDROP; return -1; } @@ -689,3 +685,15 @@ BOOL CClipboard::EmptyClipboard() const { BOOL CClipboard::IsClipboardFormatAvailable(UINT format) const { return ::IsClipboardFormatAvailable(format); } + +UINT CClipboard::EnumClipboardFormats(UINT format) const { + return ::EnumClipboardFormats(format); +} + +HGLOBAL CClipboard::GlobalAlloc(UINT uFlags, SIZE_T dwBytes) const { + return ::GlobalAlloc(uFlags, dwBytes); +} + +LPVOID CClipboard::GlobalLock(HGLOBAL hMem) const { + return ::GlobalLock(hMem); +} diff --git a/sakura_core/_os/CClipboard.h b/sakura_core/_os/CClipboard.h index f398c56796..7ceadf8159 100644 --- a/sakura_core/_os/CClipboard.h +++ b/sakura_core/_os/CClipboard.h @@ -57,6 +57,8 @@ class CClipboard{ //演算子 operator bool() const{ return m_bOpenResult!=FALSE; } //!< クリップボードを開けたならtrue + int GetDataType() const; //!< クリップボードデータ形式(CF_UNICODETEXT等)の取得 + private: HWND m_hwnd; BOOL m_bOpenResult; @@ -65,7 +67,6 @@ class CClipboard{ public: static bool HasValidData(); //!< クリップボード内に、サクラエディタで扱えるデータがあればtrue static CLIPFORMAT GetSakuraFormat(); //!< サクラエディタ独自のクリップボードデータ形式 - static int GetDataType(); //!< クリップボードデータ形式(CF_UNICODETEXT等)の取得 protected: // 単体テスト用コンストラクタ @@ -77,5 +78,8 @@ class CClipboard{ virtual HANDLE GetClipboardData(UINT uFormat) const; virtual BOOL EmptyClipboard() const; virtual BOOL IsClipboardFormatAvailable(UINT format) const; + virtual UINT EnumClipboardFormats(UINT format) const; + virtual HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes) const; + virtual LPVOID GlobalLock(HGLOBAL hMem) const; }; #endif /* SAKURA_CCLIPBOARD_4E783022_214C_4E51_A2E0_54EC343500F6_H_ */ diff --git a/tests/unittests/test-cclipboard.cpp b/tests/unittests/test-cclipboard.cpp index 4db5b13f66..def98f0b78 100644 --- a/tests/unittests/test-cclipboard.cpp +++ b/tests/unittests/test-cclipboard.cpp @@ -29,6 +29,7 @@ #define NOMINMAX #endif /* #ifndef NOMINMAX */ +#include #include #include #include @@ -38,134 +39,16 @@ #include #include +#include #include "CEol.h" #include "mem/CNativeW.h" #include "_os/CClipboard.h" using ::testing::_; +using ::testing::Invoke; using ::testing::Return; -/*! - * HWND型のスマートポインタを実現するためのdeleterクラス - */ -struct window_closer -{ - void operator()(HWND hWnd) const - { - ::DestroyWindow(hWnd); - } -}; - -//! HWND型のスマートポインタ -using windowHolder = std::unique_ptr::type, window_closer>; - - -/*! - * @brief SetHtmlTextのテスト - */ -TEST(CClipboard, SetHtmlText) -{ - constexpr const wchar_t inputData[] = L"test 109"; - constexpr const char expected[] = - "Version:0.9\r\n" - "StartHTML:00000097\r\n" - "EndHTML:00000178\r\n" - "StartFragment:00000134\r\n" - "EndFragment:00000142\r\n" - "\r\n" - "\r\n" - "test 109\r\n" - "\r\n" - "\r\n"; - - const UINT uHtmlFormat = ::RegisterClipboardFormat(L"HTML Format"); - - auto hInstance = ::GetModuleHandleW(nullptr); - if (HWND hWnd = ::CreateWindowExW(0, WC_STATICW, L"test", 0, 1, 1, 1, 1, nullptr, nullptr, hInstance, nullptr); hWnd) { - // HWNDをスマートポインタに入れる - windowHolder holder(hWnd); - - // クリップボード操作クラスでSetHtmlTextする - CClipboard cClipBoard(hWnd); - - // 操作は失敗しないはず。 - ASSERT_TRUE(cClipBoard.SetHtmlText(inputData)); - - // 操作に成功するとHTML形式のデータを利用できるはず。 - ASSERT_TRUE(::IsClipboardFormatAvailable(uHtmlFormat)); - - // クリップボード操作クラスが対応してないので生APIを呼んで確認する。 - - // グローバルメモリをロックできた場合のみ中身を取得しに行く - if (HGLOBAL hClipData = ::GetClipboardData(uHtmlFormat); hClipData != nullptr) { - // データをstd::stringにコピーする - const size_t cchData = ::GlobalSize(hClipData); - const char* pData = (char*)::GlobalLock(hClipData); - std::string strClipData(pData, cchData); - - // 使い終わったらロック解除する - ::GlobalUnlock(hClipData); - - ASSERT_STREQ(expected, strClipData.c_str()); - } - else { - FAIL(); - } - } -} - -class CClipboardTestFixture : public testing::Test { -protected: - void SetUp() override { - hInstance = ::GetModuleHandle(nullptr); - hWnd = ::CreateWindowExW(0, WC_STATICW, L"test", 0, 1, 1, 1, 1, nullptr, nullptr, hInstance, nullptr); - if (!hWnd) FAIL(); - } - void TearDown() override { - if (hWnd) - ::DestroyWindow(hWnd); - } - - HINSTANCE hInstance = nullptr; - HWND hWnd = nullptr; -}; - -TEST_F(CClipboardTestFixture, SetTextAndGetText) -{ - const std::wstring_view text = L"てすと"; - CClipboard clipboard(hWnd); - CNativeW buffer; - bool column; - bool line; - CEol eol(EEolType::cr_and_lf); - - // テストを実行する前にクリップボードの内容を破棄しておく。 - clipboard.Empty(); - - // テキストを設定する(矩形選択フラグなし・行選択フラグなし) - EXPECT_TRUE(clipboard.SetText(text.data(), text.length(), false, false, -1)); - EXPECT_TRUE(CClipboard::HasValidData()); - // Unicode文字列を取得する - EXPECT_TRUE(clipboard.GetText(&buffer, &column, &line, eol, CF_UNICODETEXT)); - EXPECT_STREQ(buffer.GetStringPtr(), text.data()); - EXPECT_FALSE(column); - EXPECT_FALSE(line); - - clipboard.Empty(); - - // テキストを設定する(矩形選択あり・行選択あり) - EXPECT_TRUE(clipboard.SetText(text.data(), text.length(), true, true, -1)); - EXPECT_TRUE(CClipboard::HasValidData()); - // サクラエディタ独自形式データを取得する - EXPECT_TRUE(clipboard.GetText(&buffer, &column, nullptr, eol, CClipboard::GetSakuraFormat())); - EXPECT_STREQ(buffer.GetStringPtr(), text.data()); - EXPECT_TRUE(column); - EXPECT_TRUE(clipboard.GetText(&buffer, nullptr, &line, eol, CClipboard::GetSakuraFormat())); - EXPECT_STREQ(buffer.GetStringPtr(), text.data()); - EXPECT_TRUE(line); -} - // グローバルメモリに書き込まれた特定の Unicode 文字列にマッチする述語関数 MATCHER_P(WideStringInGlobalMemory, expected_string, "") { const wchar_t* s = (const wchar_t*)::GlobalLock(arg); @@ -207,6 +90,17 @@ MATCHER_P(ByteValueInGlobalMemory, value, "") { return match; } +// グローバルメモリに書き込まれた特定のバイト列にマッチする述語関数 +MATCHER_P2(BytesInGlobalMemory, bytes, size, "") { + if (size != ::GlobalSize(arg)) + return false; + void* p = ::GlobalLock(arg); + if (!p) return false; + bool match = std::memcmp(p, bytes, size) == 0; + ::GlobalUnlock(arg); + return match; +} + class MockCClipboard : public CClipboard { public: MockCClipboard(bool openStatus = true) : CClipboard(openStatus) {} @@ -215,6 +109,9 @@ class MockCClipboard : public CClipboard { MOCK_CONST_METHOD1(GetClipboardData, HANDLE (UINT)); MOCK_CONST_METHOD0(EmptyClipboard, BOOL ()); MOCK_CONST_METHOD1(IsClipboardFormatAvailable, BOOL (UINT)); + MOCK_CONST_METHOD1(EnumClipboardFormats, UINT (UINT)); + MOCK_CONST_METHOD2(GlobalAlloc, HGLOBAL (UINT, SIZE_T)); + MOCK_CONST_METHOD1(GlobalLock, LPVOID (HGLOBAL)); }; // Empty のテスト。 @@ -269,6 +166,7 @@ TEST(CClipboard, SetText1) { TEST(CClipboard, SetText2) { constexpr std::wstring_view text = L"てすと"; MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, _)).WillByDefault(Invoke(::GlobalAlloc)); EXPECT_CALL(clipboard, SetClipboardData(CF_UNICODETEXT, WideStringInGlobalMemory(text))); EXPECT_CALL(clipboard, SetClipboardData(::RegisterClipboardFormat(L"MSDEVColumnSelect"), ByteValueInGlobalMemory(0))); EXPECT_FALSE(clipboard.SetText(text.data(), text.length(), true, false, CF_UNICODETEXT)); @@ -279,6 +177,7 @@ TEST(CClipboard, SetText3) { constexpr std::wstring_view text = L"てすと"; const CLIPFORMAT sakuraFormat = CClipboard::GetSakuraFormat(); MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, _)).WillByDefault(Invoke(::GlobalAlloc)); EXPECT_CALL(clipboard, SetClipboardData(sakuraFormat, SakuraFormatInGlobalMemory(text))); EXPECT_CALL(clipboard, SetClipboardData(::RegisterClipboardFormat(L"MSDEVLineSelect"), ByteValueInGlobalMemory(1))); EXPECT_CALL(clipboard, SetClipboardData(::RegisterClipboardFormat(L"VisualStudioEditorOperationsLineCutCopyClipboardTag"), ByteValueInGlobalMemory(1))); @@ -294,6 +193,22 @@ TEST(CClipboard, SetText4) { EXPECT_FALSE(clipboard.SetText(text.data(), text.length(), false, false, -1)); } +// SetText のテスト。矩形選択マークの設定に失敗した場合。 +TEST(CClipboard, SetText5) { + constexpr std::wstring_view text = L"てすと"; + MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, 1)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.SetText(text.data(), text.length(), true, false, 0)); +} + +// SetText のテスト。行選択マークの設定に失敗した場合。 +TEST(CClipboard, SetText6) { + constexpr std::wstring_view text = L"てすと"; + MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, 1)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.SetText(text.data(), text.length(), false, true, 0)); +} + // グローバルメモリを RAII で管理する簡易ヘルパークラス class GlobalMemory { public: @@ -381,8 +296,30 @@ TEST_F(CClipboardGetText, NoSpecifiedFormat4) { } // サクラ形式とCF_UNICODETEXTとCF_OEMTEXTが失敗した場合、CF_HDROPを取得する。 -TEST_F(CClipboardGetText, DISABLED_NoSpecifiedFormat5) { - // 適切なダミーデータを用意するのが難しいため未実装 +TEST_F(CClipboardGetText, NoSpecifiedFormat5) { + constexpr std::array files = {"CF_HDROP\0"}; + GlobalMemory mem(GMEM_MOVEABLE, sizeof(DROPFILES) + files.size()); + mem.Lock([=](DROPFILES* d) { + d->pFiles = sizeof(DROPFILES); + d->fWide = FALSE; + memcpy((char*)d + sizeof(DROPFILES), files.data(), files.size()); + }); + ON_CALL(clipboard, IsClipboardFormatAvailable(sakuraFormat)).WillByDefault(Return(FALSE)); + ON_CALL(clipboard, GetClipboardData(CF_UNICODETEXT)).WillByDefault(Return(nullptr)); + ON_CALL(clipboard, GetClipboardData(CF_OEMTEXT)).WillByDefault(Return(nullptr)); + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_HDROP)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(CF_HDROP)).WillByDefault(Return(mem.Get())); + EXPECT_TRUE(clipboard.GetText(&buffer, nullptr, nullptr, eol, -1)); + EXPECT_STREQ(buffer.GetStringPtr(), L"CF_HDROP"); +} + +// ここまでのすべてに失敗した場合、falseを返す。 +TEST_F(CClipboardGetText, NoSpecifiedFormat6) { + ON_CALL(clipboard, IsClipboardFormatAvailable(sakuraFormat)).WillByDefault(Return(FALSE)); + ON_CALL(clipboard, GetClipboardData(CF_UNICODETEXT)).WillByDefault(Return(nullptr)); + ON_CALL(clipboard, GetClipboardData(CF_OEMTEXT)).WillByDefault(Return(nullptr)); + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_HDROP)).WillByDefault(Return(FALSE)); + EXPECT_FALSE(clipboard.GetText(&buffer, nullptr, nullptr, eol, -1)); } // GetText で取得したいデータ形式が指定されている場合、他のデータ形式は無視する。 @@ -428,6 +365,343 @@ TEST_F(CClipboardGetText, OemTextFailure) { } // CF_HDROP を指定して取得する。 -TEST_F(CClipboardGetText, DISABLED_HDropSuccess) { - // 適切なダミーデータを用意するのが難しいため未実装 +// 取得したファイルが1つであれば末尾に改行を付加しない。 +TEST_F(CClipboardGetText, HDropSuccessSingleFile) { + constexpr std::array files = {"file\0"}; + GlobalMemory mem(GMEM_MOVEABLE, sizeof(DROPFILES) + files.size()); + mem.Lock([=](DROPFILES* d) { + d->pFiles = sizeof(DROPFILES); + d->fWide = FALSE; + memcpy((char*)d + sizeof(DROPFILES), files.data(), files.size()); + }); + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_HDROP)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(CF_HDROP)).WillByDefault(Return(mem.Get())); + EXPECT_TRUE(clipboard.GetText(&buffer, nullptr, nullptr, eol, CF_HDROP)); + EXPECT_STREQ(buffer.GetStringPtr(), L"file"); +} + +// CF_HDROP を指定して取得する。 +// 取得したファイルが複数であればすべてのファイル名の末尾に改行を付加する。 +TEST_F(CClipboardGetText, HDropSuccessMultipleFiles) { + constexpr std::array files = {"file1\0file2\0"}; + GlobalMemory mem(GMEM_MOVEABLE, sizeof(DROPFILES) + files.size()); + mem.Lock([=](DROPFILES* d) { + d->pFiles = sizeof(DROPFILES); + d->fWide = FALSE; + memcpy((char*)d + sizeof(DROPFILES), files.data(), files.size()); + }); + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_HDROP)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(CF_HDROP)).WillByDefault(Return(mem.Get())); + EXPECT_TRUE(clipboard.GetText(&buffer, nullptr, nullptr, eol, CF_HDROP)); + EXPECT_STREQ(buffer.GetStringPtr(), L"file1\r\nfile2\r\n"); +} + +// CF_HDROP が指定されているが取得に失敗した場合。 +TEST_F(CClipboardGetText, HDropFailure) { + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_HDROP)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(CF_HDROP)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.GetText(&buffer, nullptr, nullptr, eol, CF_HDROP)); +} + +// 矩形選択マーク取得のテスト。 +TEST_F(CClipboardGetText, ColumnSelectIsFalse) { + ON_CALL(clipboard, EnumClipboardFormats(0)).WillByDefault(Return(0)); + bool column; + clipboard.GetText(&buffer, &column, nullptr, eol); + EXPECT_FALSE(column); +} + +TEST_F(CClipboardGetText, ColumnSelectIsTrue) { + UINT format = RegisterClipboardFormatW(L"MSDEVColumnSelect"); + ON_CALL(clipboard, EnumClipboardFormats(0)).WillByDefault(Return(format)); + bool column; + clipboard.GetText(&buffer, &column, nullptr, eol); + EXPECT_TRUE(column); +} + +// 行選択マーク取得のテスト。 +TEST_F(CClipboardGetText, LineSelectIsFalse) { + ON_CALL(clipboard, EnumClipboardFormats(0)).WillByDefault(Return(0)); + bool line; + clipboard.GetText(&buffer, &line, nullptr, eol); + EXPECT_FALSE(line); +} + +TEST_F(CClipboardGetText, LineSelectIsTrue1) { + UINT format = RegisterClipboardFormatW(L"MSDEVLineSelect"); + ON_CALL(clipboard, EnumClipboardFormats(0)).WillByDefault(Return(format)); + bool line; + clipboard.GetText(&buffer, nullptr, &line, eol); + EXPECT_TRUE(line); +} + +TEST_F(CClipboardGetText, LineSelectIsTrue2) { + UINT format = RegisterClipboardFormatW(L"VisualStudioEditorOperationsLineCutCopyClipboardTag"); + ON_CALL(clipboard, EnumClipboardFormats(0)).WillByDefault(Return(format)); + bool line; + clipboard.GetText(&buffer, nullptr, &line, eol); + EXPECT_TRUE(line); +} + +// GetClipboardByFormatのモード-2のテスト。GetTextと同じ処理が行われることを期待する。 +// CF_UNICODETEXTの取得に成功した場合。 +TEST_F(CClipboardGetText, GetClipboardByFormatSuccess) { + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_UNICODETEXT)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(CF_UNICODETEXT)).WillByDefault(Return(unicodeMemory.Get())); + EXPECT_TRUE(clipboard.GetClipboradByFormat(buffer, L"CF_UNICODETEXT", -2, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), unicodeText.data()); +} + +// GetClipboardDataが失敗した場合。 +TEST_F(CClipboardGetText, GetClipboardByFormatFailure1) { + buffer.SetString(L"dummy"); + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_UNICODETEXT)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(_)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.GetClipboradByFormat(buffer, L"CF_UNICODETEXT", -2, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L""); +} + +// 取得できるフォーマットがクリップボード内になかった場合。 +TEST_F(CClipboardGetText, GetClipboardByFormatFailure2) { + buffer.SetString(L"dummy"); + ON_CALL(clipboard, IsClipboardFormatAvailable(CF_UNICODETEXT)).WillByDefault(Return(FALSE)); + EXPECT_FALSE(clipboard.GetClipboradByFormat(buffer, L"CF_UNICODETEXT", -2, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L""); +} + +// OpenClipboard が失敗していた場合。 +TEST(CClipboard, GetTextNoClipboard) { + CNativeW buffer; + CEol eol; + MockCClipboard clipboard(false); + EXPECT_FALSE(clipboard.GetText(&buffer, nullptr, nullptr, eol)); +} + +struct ClipboardFormatDefinition { + CLIPFORMAT id; + const wchar_t* const name; +}; + +const std::array KNOWN_FORMATS = { + { + {CF_TEXT ,L"CF_TEXT"}, + {CF_BITMAP ,L"CF_BITMAP"}, + {CF_METAFILEPICT,L"CF_METAFILEPICT"}, + {CF_SYLK ,L"CF_SYLK"}, + {CF_DIF ,L"CF_DIF"}, + {CF_TIFF ,L"CF_TIFF"}, + {CF_OEMTEXT ,L"CF_OEMTEXT"}, + {CF_DIB ,L"CF_DIB"}, + {CF_PALETTE ,L"CF_PALETTE"}, + {CF_PENDATA ,L"CF_PENDATA"}, + {CF_RIFF ,L"CF_RIFF"}, + {CF_WAVE ,L"CF_WAVE"}, + {CF_UNICODETEXT ,L"CF_UNICODETEXT"}, + {CF_ENHMETAFILE ,L"CF_ENHMETAFILE"}, + {CF_HDROP ,L"CF_HDROP"}, + {CF_LOCALE ,L"CF_LOCALE"}, + {CF_DIBV5 ,L"CF_DIBV5"} + } +}; + +const wchar_t* const UNITTEST_FORMAT_NAME = L"123SakuraUnittest"; + +// 標準フォーマットを指定した場合 +TEST(CClipboard, IsIncludeClipboardFormat1) { + for (auto format : KNOWN_FORMATS) { + MockCClipboard clipboard; + ON_CALL(clipboard, IsClipboardFormatAvailable(format.id)).WillByDefault(Return(TRUE)); + EXPECT_TRUE(clipboard.IsIncludeClipboradFormat(format.name)); + } +} + +// 数値を指定した場合 +TEST(CClipboard, IsIncludeClipboardFormat2) { + MockCClipboard clipboard; + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(TRUE)); + EXPECT_TRUE(clipboard.IsIncludeClipboradFormat(L"12345")); +} + +// 標準フォーマット以外の文字列を指定した場合 +TEST(CClipboard, IsIncludeClipboardFormat3) { + const UINT format = ::RegisterClipboardFormatW(UNITTEST_FORMAT_NAME); + + MockCClipboard clipboard; + ON_CALL(clipboard, IsClipboardFormatAvailable(format)).WillByDefault(Return(TRUE)); + EXPECT_TRUE(clipboard.IsIncludeClipboradFormat(UNITTEST_FORMAT_NAME)); +} + +// 対象フォーマットのデータが存在しなかった場合に失敗することを確認するテスト +TEST(CClipboard, IsIncludeClipboardFormat4) { + MockCClipboard clipboard; + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(FALSE)); + EXPECT_FALSE(clipboard.IsIncludeClipboradFormat(L"12345")); +} + +// フォーマット文字列が空だった場合は失敗する。 +TEST(CClipboard, IsIncludeClipboardFormat5) { + MockCClipboard clipboard; + EXPECT_FALSE(clipboard.IsIncludeClipboradFormat(L"")); +} + +// 不明なモード値を指定すると失敗する。 +TEST(CClipboard, SetClipboardByFormat1) { + MockCClipboard clipboard; + EXPECT_FALSE(clipboard.SetClipboradByFormat({L"テスト", 3}, L"12345", 99999, 1)); +} + +// フォーマット名が空文字列だと失敗する。 +TEST(CClipboard, SetClipboardByFormat2) { + MockCClipboard clipboard; + EXPECT_FALSE(clipboard.SetClipboradByFormat({L"テスト", 3}, L"", 99999, 1)); +} + +// モード-1(バイナリデータ)のテスト。 +// UTF-16 の符号単位(0x0000~0x00ff)を 0x00~0xff のバイト値にマップする。 +// 終端モード0では文字列中の \0 をバイナリとして扱う(終端として認識しない)。 +TEST(CClipboard, SetClipboardByFormat3) { + MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, _)).WillByDefault(Invoke(::GlobalAlloc)); + EXPECT_CALL(clipboard, SetClipboardData(12345, BytesInGlobalMemory("\x00\x01\xfe\xff", 4))); + EXPECT_TRUE(clipboard.SetClipboradByFormat({L"\x00\x01\xfe\xff", 4}, L"12345", -1, 0)); +} + +// モード-1(バイナリデータ)のテスト。 +// 0x100以上の値が含まれている場合は失敗する。 +TEST(CClipboard, SetClipboardByFormat4) { + MockCClipboard clipboard; + EXPECT_CALL(clipboard, SetClipboardData(_, _)).Times(0); + EXPECT_FALSE(clipboard.SetClipboradByFormat({L"\x100", 1}, L"12345", -1, 0)); +} + +// モード3(UTF-16)のテスト。コード変換を行わないパターン。 +// 終端モードの自動判定を要求する。期待されるモードは2(2バイトの0値で終端する)。 +TEST(CClipboard, SetClipboardByFormat5) { + MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, _)).WillByDefault(Invoke(::GlobalAlloc)); + EXPECT_CALL(clipboard, SetClipboardData(12345, WideStringInGlobalMemory(L"テスト"))); + EXPECT_TRUE(clipboard.SetClipboradByFormat({L"テスト", 3}, L"12345", 3, -1)); +} + +// モード4(UTF-8)のテスト。コード変換を行う。 +// 終端モードの自動判定を要求する。期待されるモードは1(1バイトの0値で終端する)。 +// +// 共有データに依存するためテスト不能。 +TEST(CClipboard, DISABLED_SetClipboardByFormat6) { + MockCClipboard clipboard; + EXPECT_CALL(clipboard, SetClipboardData(12345, AnsiStringInGlobalMemory("テスト"))); + EXPECT_TRUE(clipboard.SetClipboradByFormat({L"テスト", 3}, L"12345", 4, -1)); +} + +// モード-2のテスト。SetTextと同じ処理を行う。 +TEST(CClipboard, SetClipboardByFormat7) { + constexpr std::wstring_view text = L"テスト"; + MockCClipboard clipboard; + EXPECT_CALL(clipboard, SetClipboardData(CF_UNICODETEXT, WideStringInGlobalMemory(text))); + + // 既存のコードに実装ミスがあり、成功してもfalseを返してしまう…。 +// EXPECT_TRUE(clipboard.SetClipboradByFormat({text.data(), text.size()}, L"CF_UNICODETEXT", -2, 0)); + EXPECT_FALSE(clipboard.SetClipboradByFormat({text.data(), text.size()}, L"CF_UNICODETEXT", -2, 0)); +} + +// モード-2以外でGlobalAllocが失敗した場合。 +TEST(CClipboard, SetClipboardByFormat8) { + MockCClipboard clipboard; + ON_CALL(clipboard, GlobalAlloc(_, _)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.SetClipboradByFormat({L"テスト", 3}, L"12345", 3, -1)); +} + +// フォーマット名が空文字列だった場合は即失敗する。 +TEST(CClipboard, GetClipboardByFormat1) { + MockCClipboard clipboard; + CNativeW buffer(L"dummy"); + CEol eol(EEolType::cr_and_lf); + EXPECT_FALSE(clipboard.GetClipboradByFormat(buffer, L"", -1, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L""); +} + +// 要求されたフォーマットのデータがクリップボードに存在しなければ失敗する。 +TEST(CClipboard, GetClipboardByFormat2) { + MockCClipboard clipboard; + CNativeW buffer(L"dummy"); + CEol eol(EEolType::cr_and_lf); + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(FALSE)); + EXPECT_FALSE(clipboard.GetClipboradByFormat(buffer, L"12345", -1, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L""); +} + +// GetClipboardDataが失敗した場合。 +TEST(CClipboard, GetClipboardByFormat3) { + MockCClipboard clipboard; + CNativeW buffer(L"dummy"); + CEol eol(EEolType::cr_and_lf); + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(12345)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.GetClipboradByFormat(buffer, L"12345", -1, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L""); +} + +// GlobalLockが失敗した場合。 +TEST(CClipboard, GetClipboardByFormat4) { + MockCClipboard clipboard; + CNativeW buffer(L"dummy"); + CEol eol(EEolType::cr_and_lf); + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(12345)).WillByDefault(Return((HANDLE)67890)); + ON_CALL(clipboard, GlobalLock((HANDLE)67890)).WillByDefault(Return(nullptr)); + EXPECT_FALSE(clipboard.GetClipboradByFormat(buffer, L"12345", -1, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L""); +} + +// モード-1(バイナリデータ)のテスト。 +// 0x00~0xff のバイト値を UTF-16 の符号単位(0x0000~0x00ff)にマップする。 +// 終端モード0ではデータ中の \0 をバイナリとして扱う(終端として認識しない)。 +TEST(CClipboard, GetClipboardByFormat5) { + GlobalMemory memory(GMEM_MOVEABLE, 2); + memory.Lock([=](char* p) { + std::memcpy(p, "\x00\xff", 2); + }); + MockCClipboard clipboard; + CNativeW buffer; + CEol eol(EEolType::cr_and_lf); + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(12345)).WillByDefault(Return(memory.Get())); + ON_CALL(clipboard, GlobalLock(_)).WillByDefault(Invoke(::GlobalLock)); + EXPECT_TRUE(clipboard.GetClipboradByFormat(buffer, L"12345", -1, 0, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L"\x00\xff"); +} + +// モード3(UTF-16)のテスト。コード変換を行わないパターン。 +// 終端モードには2を設定する(2バイトの0値で終端されていることを期待する)。 +TEST(CClipboard, GetClipboardByFormat6) { + GlobalMemory memory(GMEM_MOVEABLE, sizeof(wchar_t) * 8); + memory.Lock([=](wchar_t* p) { + std::memcpy(p, L"テスト\x00データ", 8); + }); + MockCClipboard clipboard; + CNativeW buffer; + CEol eol(EEolType::cr_and_lf); + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(12345)).WillByDefault(Return(memory.Get())); + ON_CALL(clipboard, GlobalLock(_)).WillByDefault(Invoke(::GlobalLock)); + EXPECT_TRUE(clipboard.GetClipboradByFormat(buffer, L"12345", 3, 2, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L"テスト"); +} + +// モード4(UTF-8)のテスト。コード変換を行う。 +// 終端モードには1を設定する(1バイトの0値で終端されていることを期待する)。 +// +// CEditDoc のインスタンスに依存するためテスト不能。 +TEST(CClipboard, DISABLED_GetClipboardByFormat7) { + GlobalMemory memory(GMEM_MOVEABLE, 14); + memory.Lock([=](char* p) { + std::memcpy(p, "テスト\x00データ", 14); + }); + MockCClipboard clipboard; + CNativeW buffer; + CEol eol(EEolType::cr_and_lf); + ON_CALL(clipboard, IsClipboardFormatAvailable(12345)).WillByDefault(Return(TRUE)); + ON_CALL(clipboard, GetClipboardData(12345)).WillByDefault(Return(memory.Get())); + EXPECT_TRUE(clipboard.GetClipboradByFormat(buffer, L"12345", 4, 1, eol)); + EXPECT_STREQ(buffer.GetStringPtr(), L"テスト"); }