3#include <winpr/print.h>
4#include <winpr/image.h>
5#include <winpr/clipboard.h>
6#include <winpr/stream.h>
9#include <winpr/shell.h>
10#include <winpr/path.h>
12#define test_log(fmt, ...) test_log_fn(__FILE__, __func__, __LINE__, (fmt), ##__VA_ARGS__)
13static void test_log_fn(
const char* file,
const char* func,
size_t line,
const char* fmt, ...)
20 winpr_vasprintf(&fstr, &fslen, fmt, ap);
21 (void)fprintf(stderr,
"[%" PRIu32
":%s]: %s\n", line, func, fstr);
26#define test_ClipboardSetData(clipboard, formatId, data, size) \
27 test_ClipboardSetDataFn(__FILE__, __func__, __LINE__, (clipboard), (formatId), (data), (size))
30static BOOL test_ClipboardSetDataFn(
const char* file,
const char* func,
size_t line,
31 wClipboard* clipboard, UINT32 formatId,
const void* data,
34 const BOOL rc = ClipboardSetData(clipboard, formatId, data, size);
37 test_log_fn(file, func, line,
"ClipboardSetData %s[0x%04" PRIx32
"][%" PRIu32
"] failed",
38 ClipboardGetFormatName(clipboard, formatId), formatId, size);
43#define test_ClipboardGetData(clipboard, formatId, size) \
44 test_ClipboardGetDataFn(__FILE__, __func__, __LINE__, (clipboard), (formatId), (size))
47static void* test_ClipboardGetDataFn(
const char* file,
const char* func,
size_t line,
48 wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
50 void* rc = ClipboardGetData(clipboard, formatId, pSize);
53 test_log_fn(file, func, line,
"ClipboardGetData %s[0x%04" PRIx32
"] failed",
54 ClipboardGetFormatName(clipboard, formatId), formatId);
60static BOOL test_dib_to_bmp(
const BYTE* dib,
size_t dibSize,
size_t expectedOffset)
64 wClipboard* clipboard = ClipboardCreate();
67 test_log(
"ClipboardCreate failed");
71 const UINT32 bmpId = ClipboardRegisterFormat(clipboard,
"image/bmp");
74 test_log(
"bmpId == 0");
77 if (dibSize > UINT32_MAX)
79 test_log(
"dibSize %" PRIuz
" > UINT32_MAX failed");
82 if (!test_ClipboardSetData(clipboard, CF_DIB, dib, (UINT32)dibSize))
86 BYTE* bmp = test_ClipboardGetData(clipboard, bmpId, &bmpSize);
91 wImage* image = winpr_image_new();
94 test_log(
"winpr_image_new failed");
97 rc = (winpr_image_read_buffer(image, bmp, bmpSize) > 0);
99 test_log(
"winpr_image_read_buffer failed");
100 winpr_image_free(image, TRUE);
105 ClipboardDestroy(clipboard);
109static void write_dib_info_header(
wStream* s, UINT32 size, UINT16 bpp, UINT32 compression)
113 Stream_Write_UINT32(s, size);
114 Stream_Write_INT32(s, 1);
115 Stream_Write_INT32(s, 1);
116 Stream_Write_UINT16(s, 1);
117 Stream_Write_UINT16(s, bpp);
118 Stream_Write_UINT32(s, compression);
119 Stream_Write_UINT32(s, 4);
124static BOOL test_dib_offsets(
void)
127 wStream sbuffer = WINPR_C_ARRAY_INIT;
128 wStream* s = Stream_StaticInit(&sbuffer, v4,
sizeof(v4));
131 write_dib_info_header(s,
sizeof(
BITMAPV4HEADER), 32, BI_BITFIELDS);
133 Stream_Write_UINT32(s, 0x00FF0000);
134 Stream_Write_UINT32(s, 0x0000FF00);
135 Stream_Write_UINT32(s, 0x000000FF);
136 Stream_Write_UINT32(s, 0xFF000000);
138 Stream_Write_UINT32(s, 0xFF123456);
142 BYTE bitfields[
sizeof(
BITMAPINFOHEADER) + 3 *
sizeof(DWORD) + 4] = WINPR_C_ARRAY_INIT;
143 s = Stream_StaticInit(&sbuffer, bitfields,
sizeof(bitfields));
147 Stream_Write_UINT32(s, 0x00FF0000);
148 Stream_Write_UINT32(s, 0x0000FF00);
149 Stream_Write_UINT32(s, 0x000000FF);
150 Stream_Write_UINT32(s, 0xFF123456);
151 if (!test_dib_to_bmp(bitfields,
sizeof(bitfields),
157 s = Stream_StaticInit(&sbuffer, paletted,
sizeof(paletted));
161 Stream_Zero(s, 256 *
sizeof(
RGBQUAD));
162 Stream_Write_UINT32(s, 0);
163 return test_dib_to_bmp(paletted,
sizeof(paletted),
169static BOOL test_text_leading_newline(
void)
171 const char* tests[][2] = { {
"\nfoo",
"\r\nfoo" }, {
"\n",
"\r\n" } };
174 wClipboard* clipboard = ClipboardCreate();
177 test_log(
"ClipboardCreate failed");
181 const UINT32 textId = ClipboardRegisterFormat(clipboard,
"text/plain");
182 for (
size_t x = 0; x < ARRAYSIZE(tests); x++)
184 const char** cur = tests[x];
186 if (!test_ClipboardSetData(clipboard, textId, cur[0], (UINT32)strlen(cur[0])))
190 WCHAR* wstr = test_ClipboardGetData(clipboard, CF_UNICODETEXT, &size);
191 char* str = ConvertWCharNToUtf8Alloc(wstr, size /
sizeof(WCHAR),
nullptr);
192 const BOOL match = str && (strcmp(str, cur[1]) == 0);
195 test_log(
"text/plain to CF_UNICODETEXT failed for case %" PRIuz
"[%s (%" PRIu32
196 ") %p : expect %s (%" PRIuz
") : from %s (" PRIuz
")]",
197 x, str, size, wstr, cur[1], strlen(cur[1]), cur[0], strlen(cur[0]));
207 ClipboardDestroy(clipboard);
212static BOOL test_text_conversion(
void)
214 wClipboard* clipboard = ClipboardCreate();
229 const test_case_t tests[] = {
231 { ClipboardRegisterFormat(clipboard,
"text/plain"),
232 ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
"a\nb\ncሴ՞", 10,
234 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
235 ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\r\nb\nc՞՞", 10,
"a\r\nb\nc՞՞", 11 },
236 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_UNICODETEXT,
"a\nb\ncሴ՞", 10,
237 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00",
239 { CF_UNICODETEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
240 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00", 18,
242 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_TEXT,
"a\nb\ncሴ՞", 10,
243 "a\nb\nc\\u1234\\u055e", 18 },
244 { CF_TEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\nc\\u1234\\u055e", 17,
246 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_OEMTEXT,
"a\nb\ncሴ՞", 10,
247 "a\nb\nc\\u1234\\u055e", 18 },
248 { CF_OEMTEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\rc\\u1234\\u055e", 17,
251 { ClipboardRegisterFormat(clipboard,
"text/plain"),
252 ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
"a\nb\rc\\u1234\\u055e",
253 17,
"a\nb\ncሴ՞", 11 },
254 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
255 ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\r\nb\nc՞՞", 10,
256 "a\r\nb\nc\\u055e\\u055e", 19 },
257 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_UNICODETEXT,
"a\nb\rc\\u1234\\u055e",
258 17,
"\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00",
260 { CF_UNICODETEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
261 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00", 18,
262 "a\nb\nc\\u1234\\u055e", 18 },
263 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_TEXT,
"a\nb\rc\\u1234\\u055e", 17,
264 "a\nb\nc\\u1234\\u055e", 18 },
265 { CF_TEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\nc\\u1234\\u055e", 17,
266 "a\nb\nc\\u1234\\u055e", 18 },
267 { ClipboardRegisterFormat(clipboard,
"text/plain"), CF_OEMTEXT,
"a\nb\rc\\u1234\\u055e", 17,
268 "a\nb\nc\\u1234\\u055e", 18 },
269 { CF_OEMTEXT, ClipboardRegisterFormat(clipboard,
"text/plain"),
"a\nb\rc\\u1234\\u055e", 17,
270 "a\nb\nc\\u1234\\u055e", 18 },
273 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"),
274 ClipboardRegisterFormat(clipboard,
"UTF8_STRING"),
"ab", 3,
"ab", 3 },
275 { ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8"), CF_UNICODETEXT,
"ab", 3,
276 "\x61\x00\x62\x00\x00\x00", 6 }
281 for (
size_t x = 0; x < ARRAYSIZE(tests); x++)
283 const test_case_t* cur = &tests[x];
286 if (!test_ClipboardSetData(clipboard, cur->srcFormat, cur->src, cur->srcLen))
290 BYTE* data = test_ClipboardGetData(clipboard, cur->dstFormat, &dstSize);
291 if (dstSize != cur->expectLen)
293 winpr_znfree(data, dstSize);
300 winpr_znfree(data, dstSize);
303 const int rc = memcmp(cur->expect, data, cur->expectLen);
304 winpr_znfree(data, dstSize);
309 winpr_znfree(data, dstSize);
314 ClipboardDestroy(clipboard);
319static BOOL set_descriptor_name(
FILEDESCRIPTORW* dsc,
const char* name,
const char* trailing)
323 memset(dsc->cFileName, 0xff,
sizeof(dsc->cFileName));
324 const size_t len = strlen(name);
325 if (ConvertUtf8NToWChar(name, len + 1, dsc->cFileName, ARRAYSIZE(dsc->cFileName)) < 0)
327 else if (trailing && (ConvertUtf8NToWChar(trailing, strlen(trailing), &dsc->cFileName[len],
328 ARRAYSIZE(dsc->cFileName) - len - 1) < 0))
331 test_log(
"set_descriptor_name(%s, %s) failed", name, trailing);
336static BOOL test_file_descriptors_to_uri_list(
void)
339 char* basePath = GetKnownSubPath(KNOWN_PATH_TEMP,
"frdp-test");
342 char* uris =
nullptr;
348 } data = WINPR_C_ARRAY_INIT;
350 char* cmpdir =
nullptr;
351 char* cmpa =
nullptr;
352 wClipboard* clipboard = ClipboardCreate();
356 HRESULT hr = PathCchConvertStyleA(basePath, strlen(basePath), PATH_STYLE_UNIX);
360 wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard);
363 delegate->basePath = basePath;
366 winpr_asprintf(&cmpdir, &len,
"file://%s/dir\r\n", basePath);
367 winpr_asprintf(&cmpa, &len,
"file://%s/a.txt", basePath);
368 if (!cmpdir || !cmpa)
372 winpr_Data_Write_UINT32(&data.count, ARRAYSIZE(data.dsc));
373 winpr_Data_Write_UINT32(&data.dsc[0].dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
374 if (!set_descriptor_name(&data.dsc[0],
"dir",
nullptr))
376 if (!set_descriptor_name(&data.dsc[1],
"dir\\b.txt",
nullptr))
379 if (!set_descriptor_name(&data.dsc[2],
"a.txt",
"old\\name"))
381 if (!set_descriptor_name(&data.dsc[3],
"a.txt",
"old\\"))
385 const UINT32 srcId = ClipboardRegisterFormat(clipboard,
"FileGroupDescriptorW");
386 const UINT32 dstId = ClipboardRegisterFormat(clipboard,
"text/uri-list");
387 if (!test_ClipboardSetData(clipboard, srcId, &data,
sizeof(data)))
391 uris = test_ClipboardGetData(clipboard, dstId, &size);
394 test_log(
"text/uri-list: '%s'", uris);
396 if (!strstr(uris, cmpdir))
398 test_log(
"strstr(%s, %s) failed", uris, cmpdir);
401 if (strstr(uris,
"b.txt"))
403 test_log(
"strstr(%s, %s) failed", uris,
"b.txt");
411 test_log(
"FileGroupDescriptorW -> text/uri-list conversion failed");
416 ClipboardDestroy(clipboard);
422static BOOL test_is_plain_bmp(
const BYTE* bmp,
size_t size)
424 if ((size < 54) || (bmp[0] !=
'B') || (bmp[1] !=
'M'))
427 const UINT32 biSize = winpr_Data_Get_UINT32(&bmp[14]);
428 const INT32 biHeight = winpr_Data_Get_INT32(&bmp[22]);
429 const UINT32 biCompression = winpr_Data_Get_UINT32(&bmp[30]);
430 if ((biSize != 40) || (biHeight <= 0) || (biCompression != BI_RGB))
432 test_log(
"not a plain bottom-up BMP: biSize=%" PRIu32
" biHeight=%" PRId32
433 " biCompression=%" PRIu32,
434 biSize, biHeight, biCompression);
442static BOOL test_convert_image(wClipboard* clipboard,
const char* dstMime,
const wImage* expected)
446 wImage* img = winpr_image_new();
448 test_ClipboardGetData(clipboard, ClipboardRegisterFormat(clipboard, dstMime), &size);
452 if (winpr_image_read_buffer(img, data, size) <= 0)
454 test_log(
"can not read %s", dstMime);
458 if (!winpr_image_equal(expected, img,
459 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
460 WINPR_IMAGE_CMP_FUZZY))
462 test_log(
"%s differs from the source image", dstMime);
466 if ((strcmp(dstMime,
"image/bmp") == 0) && !test_is_plain_bmp(data, size))
472 winpr_image_free(img, TRUE);
478static BOOL test_image_to_image(
void)
481 void* data =
nullptr;
483 wClipboard* clipboard = ClipboardCreate();
484 wImage* img = winpr_image_new();
485 if (!clipboard || !img)
488 if (winpr_image_read(img, TEST_CLIP_BMP) <= 0)
491 data = winpr_image_write_buffer(img, WINPR_IMAGE_BITMAP, &size);
492 if (!data || !test_ClipboardSetData(clipboard, ClipboardRegisterFormat(clipboard,
"image/bmp"),
498 if (!test_convert_image(clipboard,
"image/x-bmp", img))
500#if defined(WINPR_UTILS_IMAGE_PNG)
501 if (!test_convert_image(clipboard,
"image/png", img))
504 data = winpr_image_write_buffer(img, WINPR_IMAGE_PNG, &size);
505 if (!data || !test_ClipboardSetData(clipboard, ClipboardRegisterFormat(clipboard,
"image/png"),
509 if (!test_convert_image(clipboard,
"image/bmp", img))
516 winpr_image_free(img, TRUE);
517 ClipboardDestroy(clipboard);
523static BOOL test_text_plain(
void)
526 const char utf8[] =
"A\xe4\xb8\xad\xe6\x96\x87\xc3\xa9";
527 const size_t utf8len = strlen(utf8);
528 char* plain =
nullptr;
529 WCHAR* wstr =
nullptr;
530 char* back =
nullptr;
533 wClipboard* clipboard = ClipboardCreate();
537 const UINT32 idUtf8 = ClipboardRegisterFormat(clipboard,
"text/plain;charset=utf-8");
538 const UINT32 idPlain = ClipboardRegisterFormat(clipboard,
"text/plain");
539 if (!test_ClipboardSetData(clipboard, idUtf8, utf8, (UINT32)utf8len))
542 plain = test_ClipboardGetData(clipboard, idPlain, &size);
547 if ((size < utf8len) || (memcmp(plain, utf8, utf8len) != 0))
549 test_log(
"text/plain is not UTF-8");
553 for (UINT32 x = 0; x < size; x++)
555 if ((BYTE)plain[x] > 0x7f)
557 test_log(
"text/plain is not ASCII");
563 if (!test_ClipboardSetData(clipboard, idPlain, plain, size))
566 wstr = test_ClipboardGetData(clipboard, CF_UNICODETEXT, &size);
572 back = ConvertWCharNToUtf8Alloc(wstr, size /
sizeof(WCHAR), &backlen);
573 if (!back || (backlen < utf8len) || (memcmp(back, utf8, utf8len) != 0))
575 test_log(
"text/plain does not convert back to the original text");
585 ClipboardDestroy(clipboard);
589static BOOL test_cf_locale(
void)
592 wClipboard* cb = ClipboardCreate();
596 const UINT32 formats[] = { CF_OEMTEXT, CF_TEXT, CF_UNICODETEXT,
597 ClipboardRegisterFormat(cb,
"text/plain"),
598 ClipboardRegisterFormat(cb,
"text/plain;charset=utf-8") };
600 for (
size_t x = 0; x < ARRAYSIZE(formats); x++)
602 const UINT32 format = formats[x];
604 const char text[] =
"some test string";
605 if (!test_ClipboardSetData(cb, format, text,
sizeof(text)))
608 void* locale = test_ClipboardGetData(cb, CF_LOCALE, &size);
609 if (!locale || (size != 4))
614 const UINT32 loc = *(UINT32*)locale;
621 ClipboardDestroy(cb);
625int TestClipboardFormats(
int argc,
char* argv[])
629 UINT32* pFormatIds =
nullptr;
630 const char* formatName =
nullptr;
631 UINT32 utf8StringFormatId = 0;
636 if (!test_cf_locale())
638 if (!test_text_leading_newline())
641 if (!test_text_conversion())
644 if (!test_file_descriptors_to_uri_list())
647 wClipboard* clipboard = ClipboardCreate();
651 const char* mime_types[] = {
"text/plain",
"text/plain;charset=utf-8",
652 "text/html",
"image/bmp",
653 "image/png",
"image/webp",
655 for (
size_t x = 0; x < ARRAYSIZE(mime_types); x++)
657 const char* mime = mime_types[x];
658 UINT32
id = ClipboardRegisterFormat(clipboard, mime);
659 test_log(
"ClipboardRegisterFormat(%s) -> 0x%08" PRIx32
"", mime,
id);
664 utf8StringFormatId = ClipboardRegisterFormat(clipboard,
"UTF8_STRING");
665 pFormatIds =
nullptr;
666 count = ClipboardGetRegisteredFormatIds(clipboard, &pFormatIds);
668 for (UINT32 index = 0; index < count; index++)
670 UINT32 formatId = pFormatIds[index];
671 formatName = ClipboardGetFormatName(clipboard, formatId);
672 test_log(
"Format: 0x%08" PRIX32
" %s", formatId, formatName);
679 const char pSrcData[] =
"this is a test string";
680 char* pDstData =
nullptr;
682 UINT32 SrcSize = (UINT32)(strnlen(pSrcData, ARRAYSIZE(pSrcData)) + 1);
683 const BOOL bSuccess =
684 test_ClipboardSetData(clipboard, utf8StringFormatId, pSrcData, SrcSize);
689 pDstData = (
char*)test_ClipboardGetData(clipboard, utf8StringFormatId, &DstSize);
690 const BOOL equal = (SrcSize == DstSize) && (strncmp(pSrcData, pDstData, SrcSize) == 0);
696 WCHAR* pDstData = (WCHAR*)test_ClipboardGetData(clipboard, CF_UNICODETEXT, &WDstSize);
698 size_t cmpSrcSize = 0;
700 ConvertWCharNToUtf8Alloc(pDstData, WDstSize /
sizeof(WCHAR), &cmpSrcSize);
702 (SrcSize == cmpSrcSize + 1ull) && (strncmp(pCmpData, pSrcData, SrcSize) == 0);
710 pFormatIds =
nullptr;
711 count = ClipboardGetFormatIds(clipboard, &pFormatIds);
713 for (UINT32 index = 0; index < count; index++)
715 UINT32 formatId = pFormatIds[index];
716 formatName = ClipboardGetFormatName(clipboard, formatId);
717 test_log(
"Format: 0x%08" PRIX32
" %s", formatId, formatName);
722 const char* name = TEST_CLIP_BMP;
723 BOOL bSuccess = FALSE;
724 UINT32 idBmp = ClipboardRegisterFormat(clipboard,
"image/bmp");
726 wImage* img = winpr_image_new();
730 if (winpr_image_read(img, name) <= 0)
732 winpr_image_free(img, TRUE);
737 void* data = winpr_image_write_buffer(img, WINPR_IMAGE_BITMAP, &bmpsize);
738 bSuccess = test_ClipboardSetData(clipboard, idBmp, data, bmpsize);
741 winpr_image_free(img, TRUE);
749 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
752 bSuccess = test_ClipboardSetData(clipboard,
id, pDstData, DstSize);
758 const uint32_t
id = ClipboardGetFormatId(clipboard,
"HTML Format");
760 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
764 FILE* fp = fopen(
"test.html",
"w");
767 (void)fwrite(pDstData, 1, DstSize, fp);
774 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/bmp");
777 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
781 if (DstSize != bmpsize)
785#if defined(WINPR_UTILS_IMAGE_PNG)
787 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/png");
790 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
796 const char* name = TEST_CLIP_PNG;
797 BOOL bSuccess = FALSE;
798 UINT32 idBmp = ClipboardRegisterFormat(clipboard,
"image/png");
800 wImage* img = winpr_image_new();
804 if (winpr_image_read(img, name) <= 0)
806 winpr_image_free(img, TRUE);
811 void* data = winpr_image_write_buffer(img, WINPR_IMAGE_PNG, &bmpsize);
812 bSuccess = test_ClipboardSetData(clipboard, idBmp, data, bmpsize);
815 winpr_image_free(img, TRUE);
823 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
826 bSuccess = test_ClipboardSetData(clipboard,
id, pDstData, DstSize);
833#if defined(WINPR_UTILS_IMAGE_WEBP)
835 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/webp");
838 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
845#if defined(WINPR_UTILS_IMAGE_JPEG)
847 UINT32
id = ClipboardRegisterFormat(clipboard,
"image/jpeg");
850 void* pDstData = test_ClipboardGetData(clipboard,
id, &DstSize);
858 if (!test_dib_offsets())
861 if (!test_image_to_image())
864 if (!test_text_plain())
871 ClipboardDestroy(clipboard);